今天项目中要下载快钱的对账单,快钱对账单文件的FTP服务器是Unix系统,connectServer方法中已连接成功,reply code:220。

但是问题是download方法中的ftpClient.listFiles(remote)不能找到具体某一文件,如果使用ftpClient.listFiles()而不具体指定某一远程文件时可以列举出所有的文件,包括remote这个需要下载的文件。且代码中的ftpClient.retrieveFile(remote, out);会出现长时间的等待,upNewStatus为false,最终会抛出:FTP response 421 received.  Server closed connection.

 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方式数据端口为20

不管哪种模式,都必须通过21这个端口建立起到FTP的管道连接,通过这个通道发送命令。

port模式:1.通过tcp的21端口建立起通道

     2.客户端在此通道发起PORT命令,并产生一个随机非特殊的端口号N(1023<N<65536)给到FTP服务器。

     3.此时客户端监听N+1端口(N+1>=1025,不一定是N端口+1),同时通过21的通道发送命令通知FTP服务器客户点通过此端口接受数据传输。

     4.FTP服务器接收到上一步的响应后通过自己的数据源端口20,去链接远程的客户端的N+1端口(此时是FTP服务端主动发起的一个端口链接)

     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

FTP下载时连接正常获取不到数据的更多相关文章

  1. ftp 下载时防止从缓存中获取文件

    //http://baike.baidu.com/link?url=QucJiA_Fg_-rJI9D4G4Z4687HG4CfhtmBUd5TlXrcWCeIEXCZxIh0TD7ng1wROAzAu ...

  2. RubyGem 下载时连接失败的解决方法

    RubyGem 下载 gem 包失败,有一定原因是 https 导致的. 搜索了很久,找到一个解决的方法. 1.下载 cacert.pem,也就是 curl 的证书. http://curl.haxx ...

  3. http和ftp下载的区别

    HTTP和FTP是两种网络传输协议的缩写,FTP是File Transportation Protocol(文件传输协议)的缩写,而HTTP则是Hyper Text Transportation Pr ...

  4. 速战速决 (6) - PHP: 获取 http 请求数据, 获取 get 数据 和 post 数据, json 字符串与对象之间的相互转换

    [源码下载] 速战速决 (6) - PHP: 获取 http 请求数据, 获取 get 数据 和 post 数据, json 字符串与对象之间的相互转换 作者:webabcd 介绍速战速决 之 PHP ...

  5. .Net 连接FTP下载文件报错:System.InvalidOperationException: The requested FTP command is not supported when using HTTP proxy

    系统环境: Windows + .Net Framework 4.0   问题描述: C#连接FTP下载文件时,在部分电脑上有异常报错,在一部分电脑上是正常的:异常报错的信息:System.Inval ...

  6. Java使用SFTP和FTP两种连接方式实现对服务器的上传下载 【我改】

    []如何区分是需要使用SFTP还是FTP? []我觉得: 1.看是否已知私钥. SFTP 和 FTP 最主要的区别就是 SFTP 有私钥,也就是在创建连接对象时,SFTP 除了用户名和密码外还需要知道 ...

  7. JDBC 连接mysql获取中文时的乱码问题

    前段时间学习JDBC,要连接mysql获取数据.按照老师的样例数据,要存一些名字之类的信息,用的都是英文名,我当时就不太想用英文,就把我室友的名字存了进去,嘿嘿,结果,出问题了. 连接数据库语句: s ...

  8. ftp下载目录下所有文件及文件夹内(递归)

    ftp下载目录下所有文件及文件夹内(递归)   /// <summary> /// ftp文件上传.下载操作类 /// </summary> public class FTPH ...

  9. java ftp下载文件

    1.使用官方正规的jar commons-net-1.4.1.jar jakarta-oro-2.0.8.jar 注意:使用ftp从windows服务器下载文件和从linux服务器下载文件不一样 2. ...

随机推荐

  1. 免费的Lucene 原理与代码分析完整版下载

    Lucene是一个基于Java的高效的全文检索库.那么什么是全文检索,为什么需要全文检索?目前人们生活中出现的数据总的来说分为两类:结构化数据和非结构化数据.很容易理解,结构化数据是有固定格式和结构的 ...

  2. Andrew Ng机器学习课程笔记--week3(逻辑回归&正则化参数)

    Logistic Regression 一.内容概要 Classification and Representation Classification Hypothesis Representatio ...

  3. SQL练习题完整(做完你就是高手)

    SQL 练习题答案 一.补充作业一.   设有三个关系:                S(SNO, SNAME, AGE, SEX,Sdept)                SC(SNO, CNO ...

  4. c#中的interface abstract与virtual介绍

    abstract 与virtual : 方法重写时都使用 override 关键字,interface中的方法和abstract方法都要求实现  interface用来声明接口1.只提供一些方法规约, ...

  5. Redis集群之节点管理

    Redis集群一旦启动,就不能轻易删除掉一个节点了. 需要由redis-trib.rg这个ruby脚本行使集群管理的功能.所有的哈希槽都分配于master节点 一.delete master node ...

  6. c语言 创建链表

    #include "malloc.h" #include "stdio.h" #define LEN sizeof(struct student) typede ...

  7. C++ 类声明 类前置声明范例

    转载自http://www.cnblogs.com/staring-hxs/p/3244251.html 在编写C++程序的时候,偶尔需要用到前置声明(Forward declaration).下面的 ...

  8. 获取MVC中Controller下的Action参数异常

    我现在做的一个项目有一个这样的需求, 比如有一个页面需要一个Guid类型的参数: public ActionResult Index(Guid id) { //doing something ... ...

  9. 学习js的点点滴滴记录

    从安装完node.js后(里面自带了npm), 每个模块下都有个 package.json文件,在这个目录下打开cmd后 输入npm install 就是按照package.json里面的内容进行安装 ...

  10. DispatcherTimer和Timer(计时器)

    System.Windows.Threading.DispatcherTimer dTime;        System.Timers.Timer timer;        public Main ...