I made a small application that should upload files to an FTP server. The thing is that I used passive mode with the method
enterLocalPassiveMode()
Recently I was told that no passive mode is allowed on the FTP server, so I should make my application work in active mode. I suppose it couldn't be done by simply changing the method to
enterLocalActiveMode()
What else should I change in the application to ensure it's working in active mode.
Here's a code snippet which makes the connection to the server:
public void connect() throws FTPException {
try {
ftpClient.connect(server, port);
replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
printText("FTP server refused connection.");
throw new FTPException("FTP server refused connection.");
}
boolean logged = ftpClient.login(user, pass);
if (!logged) {
ftpClient.disconnect();
printText("Could not login to the server.");
throw new FTPException("Could not login to the server.");
}
ftpClient.enterLocalPassiveMode();
} catch (IOException ex) {
printText("I/O errortest: " + ex.getMessage());
throw new FTPException("I/O error: " + ex.getMessage());
}
}
Some guidance to what I have to change?
This is old, but I stumbled onto it trying to solve the issue myself.
You have to call enterLocalPassiveMode() after calling connect() and before calling login().
See my example below which initialises the FTPClient in local passive mode, lists files for a given directory, then closes the connections.
private static FTPClient client;
public static void main(String [] args) {
//initialise the client
initPassiveClient();
//do stuff
FTPFile [] files = listFiles("./");
if( files != null ) {
logger.info("Listing Files:");
for( FTPFile f : files) {
logger.info(f.getName());
}
}
//close the client
close();
}
/**
* getPassiveClient retrive a FTPClient object that's set to local passive mode
*
* #return FTPClient
*/
public static FTPClient initPassiveClient() {
if( client == null ) {
logger.info("Getting passive FTP client");
client = new FTPClient();
try {
client.connect(server);
// After connection attempt, you should check the reply code to verify
// success.
int reply = client.getReplyCode();
if(!FTPReply.isPositiveCompletion(reply)) {
client.disconnect();
logger.error("FTP server refused connection.");
System.exit(0);
}
//after connecting to the server set the local passive mode
client.enterLocalPassiveMode();
//send username and password to login to the server
if( !client.login(user, pass) ) {
logger.error("Could not login to FTP Server");
System.exit(0);
}
} catch (SocketException e) {
String message = "Could not form socket";
logger.error(message+"\n", e);
System.exit(0);
} catch (IOException e) {
String message = "Could not connect";
logger.error(message+"\n", e);
System.exit(0);
}
}
return client;
}
public static void close() {
if( client == null ) {
logger.error("Nothing to close, the FTPClient wasn't initialized");
return;
}
//be polite and logout & close the connection before the application finishes
try {
client.logout();
client.disconnect();
} catch (IOException e) {
String message = "Could not logout";
logger.error(message+"\n", e);
}
}
/**
* listFiles uses the FTPClient to retrieve files in the specified directory
*
* #return array of FTPFile objects
*/
private static FTPFile[] listFiles(String dir) {
if( client == null ) {
logger.error("First initialize the FTPClient by calling 'initFTPPassiveClient()'");
return null;
}
try {
logger.debug("Getting file listing for current director");
FTPFile[] files = client.listFiles(dir);
return files;
} catch (IOException e) {
String message = "";
logger.error(message+"\n", e);
}
return null;
}
http://commons.apache.org/proper/commons-net/apidocs/org/apache/commons/net/ftp/FTPClient.html#enterLocalActiveMode()
Not sure if this is the FTPClient you're using but it would appear that enterLocalActiveMode does indeed exist.
FTPClient Documentation states that
public boolean enterRemoteActiveMode(InetAddress host,int port)
throws IOException
Set the current data connection mode to ACTIVE_REMOTE_DATA_CONNECTION . Use this method only for server to server data transfers. This method issues a PORT command to the server, indicating the other server and port to which it should connect for data transfers. You must call this method before EVERY server to server transfer attempt. The FTPClient will NOT automatically continue to issue PORT commands. You also must remember to call enterLocalActiveMode() if you wish to return to the normal data connection mode.
Hope this helps!
Related
I am trying to connecting a server with FTPSClient (true implicit), port 990, and it seems the connection is ok, but it says that the file PDF inside cannot be found.
String protocol = "TLS"; // TLS / SSL
boolean isImpicit = true;
int timeoutInMillis = 3000;
FTPSClient client = new FTPSClient(protocol, isImpicit);
client.setDataTimeout(timeoutInMillis);
client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
try
{
int reply;
client.connect(server, port);
client.login(user, pass);
client.setFileType(FTP.BINARY_FILE_TYPE);
client.execPBSZ(0);
client.execPROT("P");
System.out.println("Connected to " + server + ".");
reply = client.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply))
{
client.disconnect();
System.err.println("FTP server refused connection.");
System.exit(1);
}
client.listFiles();
boolean retrieved = client.retrieveFile(Constantes.DIRECCION_FTP_PDF_FACTURAS + nombre_factura, new FileOutputStream(Constantes.DIRECCION_FTP_LOCAL_DESCARGAS + nombre_factura));
}
catch (Exception e)
{
if (client.isConnected())
{
try
{
client.disconnect();
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
System.err.println("Could not connect to server.");
e.printStackTrace();
return;
}
finally
{
System.out.println("# client disconnected");
client.disconnect();
}
}
The error I got is java.io.FileNotFoundException
I tried writing the full path since C:\ , and without it, but nothing works.
Anybody can help me?
Thanks.
EDIT: IT WORKS NOW!
The path "Program Files" contains a space and maybe FileInputStream does not manage to resolve it properly.
May give it a try to put your folder to "C:/Temp/" and test it again.
Where does this FileNotFoundException happen exactly?
I've created Java function that downloads files from FTP server. It works fine from my local machine. But I need to run it under linux server (means another host and port). And the function gives an error
The collection, array, map, iterator, or enumeration portion of a for statement cannot be null
Caused in a line with the code:
for(String f : ftpNames) {
ftpclient.retrieveFile(f, os); // os is OutputStream
}
So it doesn't see the files...
I added
ftpclient.enterRemotePassiveMode();
And ftpclient.getPassiveHost() returns 227 Entering Passive Mode (x,x,x,x,204,15)
Tried to list and download them via shell - it works.
How should I modify my code to solve the problem? Thanks.
UPD. I got log from FTP server I'm trying to get files from, and there is such string:
425 Cannot open data connection
Full code:
static boolean ftpFilesDownload(String ip, int port, String login, String passwd, String ftpdir, String localdir) throws IOException {
Boolean result = false;
FTPClient client = new FTPClient();
String separator = File.separator;
try {
client.connect(ip, port);
System.out.println(client.getReplyString());
client.login(login, passwd);
System.out.println(client.getReplyString());
client.setControlKeepAliveTimeout(1000*60*5);
client.setControlKeepAliveReplyTimeout(1000*60*5);
client.setFileType(FTP.BINARY_FILE_TYPE);
System.out.println("client setFileType success");
client.changeWorkingDirectory(ftpdir);
System.out.println(client.getReplyString());
client.printWorkingDirectory();
System.out.println("directory changed");
FTPFile[] ftpFiles = client.listFiles();
System.out.println(ftpFiles);
String[] ftpNames = client.listNames();
System.out.println("the files are " + Arrays.toString(ftpNames)); // so null here...
for(String f : ftpNames) {
String localfile = localdir + f;
OutputStream os = new FileOutputStream(localfile);
try {
result = client.retrieveFile(f, os);
System.out.println("DOWNLOADING STARTED);
System.out.println(client.getReplyString());
client.noop();
}
catch(Exception e) {
System.out.println(e);
result = false;
}
finally {
if(os != null)
os.close();
}
}
client.logout();
System.out.println(client.getReplyString());
}
catch(Exception e)
{
System.out.println(e);
result = false;
}
finally
{
try
{
client.disconnect();
}
catch(Exception e)
{
System.out.println(e);
result = false;
}
}
return result;
}
As the error message explains, you're trying to iterate over a null object. You should check for this (or make sure an empty Iterable is used perhaps)
If this is an execptional (error) state, I'd check for this explicitly and throw some kind of runtime exception, e.g.:
if (ftpNames == null) {
throw new IllegalArgumentException("Cannot use a null set of FTP servers");
}
for (String f : ftpNames) {
ftpclient.retrieveFile(f, os); // os is OutputStream
}
Alternatively you could try to continue with no FTP servers, but seems a bit pointless.
Try to use ftpclient.enterLocalActiveMode();
Context:
The following AsyncTask for an android application sends and receives so called Request objects from a server.
If the user makes changes to his stuff in the app, new request objects get generated and added to the synchronization queue. If he then hits the sync-button the AsyncTask is created and executed with his requests as parameters.
The handler finally takes all answers and sets the neccessary consequences in the database. He then finally updates the UI by calling one single method on the UI thread (onPostExecute).
public class RequestSender extends AsyncTask<Request, Void, Boolean>{
// Server data
private String host;
private int port = 1337;
private Socket socket;
private AnswerHandler handler;
public RequestSender(AnswerHandler handler) {
this.host = "hostNameHere";
this.handler = handler;
}
/**
* This method gets started as asynchronous task when you call .run()
* #return
*/
#Override
protected Boolean doInBackground(Request... requests) {
return sendAndReceive(requests);
}
private boolean sendAndReceive(Request... requests) {
boolean isConnected = this.initSocket();
if(isConnected) {
this.send(requests);
this.waitForAnswer();
} else {
handler.setRequests(requests);
}
return isConnected;
}
/**
* Tries to open a socket on the android device to a specified Host
*/
private boolean initSocket() {
try {
SocketAddress sockaddr = new InetSocketAddress(host, port);
socket = new Socket();
socket.connect(sockaddr, 5000);
return true;
} catch (UnknownHostException e) {
System.err.println("Unknown Host in initSocket()");
} catch (IOException e) {
System.err.println("Connection timed out");
}
return false;
}
/**
* Tries to send a request to the server
* #param request
*/
public void send(Request... request) {
if(socket != null) {
try {
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject(request);
out.flush();
} catch (IOException e) {
System.err.println("Couldn't write to socket in RequestSender");
}
}
}
/**
* Waits for the answer from the server and reports the result in the handler
*/
private void waitForAnswer() {
try {
socket.setSoTimeout(5000);
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Request[] answers = (Request[]) in.readObject();
socket.close();
handler.setRequests(answers);
} catch (StreamCorruptedException e) {
System.err.println("Failed to open stream from server");
} catch (IOException e) {
System.err.println("Failed to read answers from server");
} catch (ClassNotFoundException e) {
System.err.println("Failed to read class from server");
}
}
#Override
protected void onPostExecute(Boolean a) {
handler.updateUI();
}
}
Now my Problem:
The whole thing works without any problem for a few times (It depends on the goodwill of my phone how many times), but then it seems like the task gets stuck somewhere without giving me any error message on System.err.
Restarting the app solves the problem and it works again without any problem.
I already read that AsyncTasks get executed on one single thread since Honeycomb. I set a timeout on open socket and read in, so a stuck task should terminate after this timeout.
Is there any problem with my code and could you imagine a solution for this?
Recently I face this problem and after debugging a lot and brain storming for a week I finally got the bug.
Ok lets do some homework.
Process to send/receive data
Establish a connection. Let assume connectToServer() is a function that physically connects the device to the server.
The socket/TCP part. In your case you have doInbackground(), in which you are calling initSocket() to initiate a socket connetion.
In real world scenario when you request a connection to a server it takes some time, may be a one or two seconds. So you should wait for that time before initiating a socket connection request. If a socket request send before a connection then it goes to lock state and releases after the default time out is finished which make it stuck.
Programming scenario
connectToServer();
// wait for 1 or 2 second.
initSocket();
Sample code
/* Function to check whether we are physically connected to the server or not */
private boolean isConnEstablished(){
WifiInfo connInfo = mManager.getConnectionInfo();
return mManager.isWifiEnabled() && connInfo.getNetworkId() != -1 && connInfo.getIpAddress() != 0;
}
private void initSocket() {
boolean scanning = true;
int tryCount = 5; // we trying for 5 times
try {
while (scanning && tryCount > 0) {
try {
if (isConnEstablished()) {
try{
Thread.sleep(500);
}catch (InterruptedException e){
Log.e("Yo", "sleep-error");
}
tConnection = new Socket(host, port);
scanning = false;
Log.e(getClass().getName(), "Socket connection established");
}else {
throw new ConnectException();
}
} catch (ConnectException e) {
Log.e(getClass().getName(), "connecting again...");
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Log.e(getClass().getName(), "System sleep-error: " + ex.getMessage());
}
}
tryCount--;
}
}
http://commons.apache.org/proper/commons-net/apidocs/org/apache/commons/net/ftp/FTPClient.html
I noticed the example disconnects() in the finally clause, but doesn't do the same for logout()
FTPClient ftp = new FTPClient();
FTPClientConfig config = new FTPClientConfig();
config.setXXX(YYY); // change required options
ftp.configure(config );
boolean error = false;
try {
int reply;
ftp.connect("ftp.foobar.com");
System.out.println("Connected to " + server + ".");
System.out.print(ftp.getReplyString());
// After connection attempt, you should check the reply code to verify
// success.
reply = ftp.getReplyCode();
if(!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
System.err.println("FTP server refused connection.");
System.exit(1);
}
... // transfer files
ftp.logout();
} catch(IOException e) {
error = true;
e.printStackTrace();
} finally {
if(ftp.isConnected()) {
try {
ftp.disconnect();
} catch(IOException ioe) {
// do nothing
}
}
System.exit(error ? 1 : 0);
}
Anyone know why we don't need to logout() when we catch an exception?
Anyone know why we don't need to logout() when we catch an exception?
The inside code of ftp.logout() function is as follows:
public boolean logout() throws IOException
{
return FTPReply.isPositiveCompletion(quit());
}
The quit() function send a command using sendCommand(FTPCommand.QUIT) to the FTP Server. If a connection exception happens, we are likely not being able to connect with FTP Server. calling logout() will try to write to FTP server again and create resources with additional throwing exception. In addition, although disconnect() function will also throw an exception, it closes the input, output, socket and releases resources which logout() function doesn't: as it is evident from the following source code of disconnect() function:
public void disconnect() throws IOException
{
if (_socket_ != null) _socket_.close();
if (_input_ != null) _input_.close();
if (_output_ != null) _output_.close();
if (_socket_ != null) _socket_ = null;
_input_ = null;
_output_ = null;
_controlInput_ = null;
_controlOutput_ = null;
_newReplyString = false;
_replyString = null;
}
I dont know much about the FTPClient library but I believe it's safe to assume disconnecting from the server implies logging out as part of the process if applicable, considering the explanations given in the docs:
disconnect() : Closes the connection to the FTP server and restores connection parameters to the default values.
logout() : Logout of the FTP server by sending the QUIT command.
http://commons.apache.org/proper/commons-net/apidocs/org/apache/commons/net/ftp/FTPClient.html
I installed FileZilla FTP Server on my local Windows 7 machine.
I also installed FileZilla FTP client on the same machine.
Connection is successfull between both of them confirming the server and client partnership exists.
I wrote a small quick and dirtry Jsch program for connecting to the FileZilla FTP server and below is the program:
public class TestJSch {
/** Creates a new instance of TestCommonsNet */
public TestJSch() {
}
/**
* main - Unit test program
*
* #param args
* Command line arguments
*
*/
public static void main(String[] args) {
try {
String ftpHost = "127.0.0.1";
int ftpPort = 21;// 14147;
// int ftpPort = 990;// 14147;
String ftpUserName = "kedar";
String ftpPassword = "XXXXXXXXXXX";
String ftpRemoteDirectory = "C:\\KEDAR\\Java\\FTP_Folder";
String fileToTransmit = "C:\\KEDAR\\Java\\File_Folder\\Customer.txt";
String identityfile = "C:\\KEDAR\\Java\\Ftp\\certificate.crt";
//
// First Create a JSch session
//
JSch.setLogger(new MyLogger());
System.out.println("Creating session.");
JSch jsch = new JSch();
String knownHostsFilename = "C:\\Windows\\System32\\drivers\\etc\\hosts";
jsch.setKnownHosts(knownHostsFilename);
jsch.addIdentity(identityfile);
Session session = null;
Channel channel = null;
ChannelSftp c = null;
//
// Now connect and SFTP to the SFTP Server
//
try {
// Create a session sending through our username and password
session = jsch.getSession(ftpUserName, ftpHost, ftpPort);
System.out.println("Session created.");
session.setPassword(ftpPassword);
// Security.addProvider(new com.sun.crypto.provider.SunJCE());
// b
// Setup Strict HostKeyChecking to no so we dont get the
// unknown host key exception
//
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
System.out.println("Session connected.");
//
// Open the SFTP channel
//
System.out.println("Opening Channel.");
channel = session.openChannel("sftp");
channel.connect();
c = (ChannelSftp) channel;
} catch (Exception e) {
System.err.println("Unable to connect to FTP server."
+ e.toString());
throw e;
}
//
// Change to the remote directory
//
System.out.println("Changing to FTP remote dir: "
+ ftpRemoteDirectory);
c.cd(ftpRemoteDirectory);
//
// Send the file we generated
//
try {
File f = new File(fileToTransmit);
System.out.println("Storing file as remote filename: "
+ f.getName());
c.put(new FileInputStream(f), f.getName());
} catch (Exception e) {
System.err
.println("Storing remote file failed." + e.toString());
throw e;
}
//
// Disconnect from the FTP server
//
try {
c.quit();
} catch (Exception exc) {
System.err.println("Unable to disconnect from FTPserver. "
+ exc.toString());
}
} catch (Exception e) {
System.err.println("Error: " + e.toString());
}
System.out.println("Process Complete.");
System.exit(0);
}
public static class MyLogger implements com.jcraft.jsch.Logger {
static java.util.Hashtable name = new java.util.Hashtable();
static {
name.put(new Integer(DEBUG), "DEBUG: ");
name.put(new Integer(INFO), "INFO: ");
name.put(new Integer(WARN), "WARN: ");
name.put(new Integer(ERROR), "ERROR: ");
name.put(new Integer(FATAL), "FATAL: ");
}
public boolean isEnabled(int level) {
return true;
}
public void log(int level, String message) {
System.err.print(name.get(new Integer(level)));
System.err.println(message);
}
}
}
I tried running this program and below is the FTP log:
(000033)9/12/2011 13:08:53 PM - (not logged in) (127.0.0.1)> Connected, sending welcome message...
(000033)9/12/2011 13:08:53 PM - (not logged in) (127.0.0.1)> 220-FileZilla Server version 0.9.39 beta
(000033)9/12/2011 13:08:53 PM - (not logged in) (127.0.0.1)> 220-written by Tim Kosse (Tim.Kosse#gmx.de)
(000033)9/12/2011 13:08:53 PM - (not logged in) (127.0.0.1)> 220 Please visit http://sourceforge.net/projects/filezilla/
(000033)9/12/2011 13:08:53 PM - (not logged in) (127.0.0.1)> SSH-2.0-JSCH-0.1.44
(000033)9/12/2011 13:08:53 PM - (not logged in) (127.0.0.1)> 500 Syntax error, command unrecognized.
(000033)9/12/2011 13:09:54 PM - (not logged in) (127.0.0.1)> 421 Login time exceeded. Closing control connection.
(000033)9/12/2011 13:09:54 PM - (not logged in) (127.0.0.1)> disconnected.
I don't understand why the JSch program is issuing SSH-2.0-JSCH-0.1.44 command and the communication is then turned down. How do we avoid this?
JSch is not an FTP client. JSch is an SSH client (with an included SFTP implementation).
The SSH protocol is a protocol to allow secure connections to a server, for shell access, file transfer or port forwarding. For this, the server must have an SSH server (usually on port 22, but that can vary). SFTP is a binary file transfer protocol which is usually tunneled over SSH, and not related to FTP (other than by name).
If you want to use JSch to download/upload files, you need to install and activate an SSH/SFTP server on your computer (respective the computer you want to access).
For FTP, you have to use other Java libraries (Apache Commons FTPClient seems to be famous, from the questions here).
By the way, the known hosts file for JSch is a file listing the public keys of the SSH hosts, not the file listing their IP addresses (which is the Windows config file you are trying to supply here).
Use Apache commons-net FTP library.
import java.io.IOException;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
public class FTPConnectionCode {
public static void main(String[] args) {
String server = "www.website.com";
// generally ftp port is 21
int port = 21;
String user = "ftpusername";
String pass = "ftppassword";
FTPClient ftpClient = new FTPClient();
try {
ftpClient.connect(server, port);
showServerReply(ftpClient);
int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
System.out.println("Connect failed");
return;
}
boolean success = ftpClient.login(user, pass);
showServerReply(ftpClient);
if (!success) {
System.out.println("Could not login to the server");
return;
}
// Changes working directory
success = ftpClient.changeWorkingDirectory("/dir");
showServerReply(ftpClient);
if (success) {
System.out.println("Successfully changed working directory.");
} else {
System.out.println("Failed to change working directory. See server's reply.");
}
// logs out
ftpClient.logout();
ftpClient.disconnect();
} catch (IOException ex) {
System.out.println("Oops! Something wrong happened");
ex.printStackTrace();
}
}
private static void showServerReply(FTPClient ftpClient) {
String[] replies = ftpClient.getReplyStrings();
if (replies != null && replies.length > 0) {
for (String aReply : replies) {
System.out.println("SERVER: " + aReply);
}
}
}
}