今天项目中要下载快钱的对帐单,快钱对帐单文件的FTP服务器是Unix系统,connectServer方法中已链接成功,reply code:220。html
可是问题是download方法中的ftpClient.listFiles(remote)不能找到具体某一文件,若是使用ftpClient.listFiles()而不具体指定某一远程文件时能够列举出全部的文件,包括remote这个须要下载的文件。且代码中的ftpClient.retrieveFile(remote, out);会出现长时间的等待,upNewStatus为false,最终会抛出:FTP response 421 received. Server closed connection.apache
public static boolean connectServer(String host, int port, String user, String password, String defaultPath) throws SocketException, IOException { ftpClient = new FTPClient(); // org.apache.commons.net.MalformedServerReplyException: Could not parse response code. // Server Reply: SSH-2.0-OpenSSH_7.2 // ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out))); // 设置以二进制方式传输 ftpClient.setDataTimeout(5000); ftpClient.setConnectTimeout(connectTimeout); ftpClient.setControlEncoding("UTF-8"); ftpClient.connect(host, port); log.info("Connected to " + host + "."); log.info("FTP server reply code:" + ftpClient.getReplyCode()); if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { if (ftpClient.login(user, password)) { // Path is the sub-path of the FTP path if (defaultPath != null && defaultPath.length() != 0) { ftpClient.changeWorkingDirectory(defaultPath); } return true; } } disconnect(); return false; } public static File download(String remote, String local) throws IOException { log.info("remote={},local={}", remote, local); File downloadFile = null; // 检查远程文件是否存在 FTPFile[] files = ftpClient.listFiles(remote); if (files.length == 0) { log.info("远程文件不存在: {}, {}", remote, files); return downloadFile; } File f = new File(local); OutputStream out = null; if (f.exists()) { f.delete(); } try { //ftpClient.enterLocalPassiveMode(); out = new FileOutputStream(f); boolean upNewStatus = ftpClient.retrieveFile(remote, out); out.flush(); if (upNewStatus) { downloadFile = f; } } catch (Exception e) { log.error(e.getMessage(), e); } finally { if (out != null) { out.close(); } } return downloadFile; }
FTP须要在本身测试服务器开通21端口,这个已经叫运维开通了。且须要快钱把咱们这边测试服务器ip加入快钱的白名单,不然是连接不到那边的FTP服务器的。服务器
开始一直觉得是快钱那边提供的用户是否须要什么文件链接权限,定位了好久的问题,最后搜索问题才发现对FTP链接模式和原理不清楚致使。运维
首先FTP分2中模式:主动模式(port)和被动模式(pasv).FTP标准命令TCP端口号为21,Port方式数据端口为20tcp
无论哪一种模式,都必须经过21这个端口创建起到FTP的管道链接,经过这个通道发送命令。测试
port模式:1.经过tcp的21端口创建起通道spa
2.客户端在此通道发起PORT命令,并产生一个随机非特殊的端口号N(1023<N<65536)给到FTP服务器。.net
3.此时客户端监听N+1端口(N+1>=1025,不必定是N端口+1),同时经过21的通道发送命令通知FTP服务器客户点经过此端口接受数据传输。code
4.FTP服务器接收到上一步的响应后经过本身的数据源端口20,去连接远程的客户端的N+1端口(此时是FTP服务端主动发起的一个端口连接)orm
5.若是此时客户端的防火墙策略是不能随意外部连接内部服务器的端口,则会形成上一步出现数据端连接失败!
6.若是没有上一步的状况,FTP客户端则会接收到服务端响应并返回响应信息,则创建起了数据连接通道。
pasv模式:1.经过tcp的21端口创建起通道
2.但与主动方式的FTP不一样,客户端不会提交PORT命令并容许服务器来回连它的数据端口,而是提交 PASV命令.会产生两个随机非特殊的端口N(1023<N<65536) 和N+1给到FTP服务器。其中N端口跟主动模式同样,会把N给到服务端的远程的21端口。至关于FTP服务端被动接受数据端口号而不是以前port模式的主动发起链接
3.FTP服务端会则会打开N+1的端口号
4.客户端发起N+1端口的连接,并创建数据连接。
port模式:
pasv模式:
上面的图都省略了创建tcp的21端口这个步骤。
正是由于以前采用主动模式,可是测试服务器防火墙阻止了快钱发起的数据端口的链接。也就是port模式的第5步出现问题。
于是FTPClient.listFiles(remote)或者FTPClient.retrieveFile(remote)方法时获取不了数据,就中止在那里,什么反应都没有,出现假死状态。
解决办法:在调用这两个方法以前,调用FTPClient.enterLocalPassiveMode();
这个方法的意思就是每次数据链接以前,ftp client告诉ftp server:数据链接的端口号已经告诉你了,你只需被动接受数据链接的请求就行
参考:http://www.cnblogs.com/xiaohh/p/4789813.html
http://blog.csdn.net/u010154760/article/details/45458219