FTP登录成功但无法LIST和下载文件的问题排查

问题背景:

最近有一个系统对接需求,采用了古老的ftp交换文件方式来对接。于是我用了commons-net包的3.6版本来进行ftp的连接和文件的传输。连接ftp成功,登录也没问题,但是在传输文件的时候会卡住,程序没有往下走,一段时间后抛异常。传输文件的代码如下(顺便提一下如果你连都连不上,那先理清架构,问下你们运维是不是用了代理,如果用了代理,java代码里面需要设置使用代理连接):

        //初始化ftpclient
        initFtpClient();
        //切换路径
        ftpClient.changeWorkingDirectory(pathname);
        InputStream inputStream = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] dataBytes;
        try{
            //卡在下面这一行!!
            FTPFile[] ftpFiles = ftpClient.listFiles();
            for(FTPFile file : ftpFiles){
                if(file.getName().startsWith(filenamePrefix)){
                    inputStream = ftpClient.retrieveFileStream(pathname+file.getName());
                    break;
                }
            }

            byte[] buffer = new byte[1024];
            int len;
            while ((len = inputStream.read(buffer)) != -1 ) {
                baos.write(buffer, 0, len);
            }
            dataBytes = baos.toByteArray();
            return dataBytes;

        }catch (Exception e){
            //....
        }finally {
            //....
        }

排查过程:

1.登录应用服务器,执行ftp各种操作排除架构,代理服务器配置等环境问题

由于公司的网络架构有非常严格的安全限制,所以实际上,应用服务器和目标ftp服务器之间往往还要经过各种代理服务器,所以首先要做的就是登录到应用服务器使用ftp命令连接ftp服务器(具体的ip和端口号不一定就是对方直接暴露给你的ip和端口号,需要咨询运维确认)执行目录切换,文件下载,文件上传的操作,排除架构上和代理服务器配置上可能存在的问题。附这几个操作的ftp命令:

#切换到xxx目录
cd xxx 
#下载abcd.xls文件
get abcd.xls
#上传当前目录下的yyy.txt文件,当前目录可以用lcd查看
put yyy.txt

一顿操作下来,目录切换和文件上传下载都正常,那么架构环境代理配置有问题的可能性就比较小了。命令可以正常下载文件而程序又不行,那就可能是程序的问题。可是程序只是卡住,抛出了一个空指针异常,没有任何指导意义,这种时候只能祭出最终兵器:抓包。

2.抓包分析程序的行为和命令行行为上的区别,找到问题所在

由于异常信息非常有限,我们只能使用抓包的命令(命令:tcpdump),分析我写的java程序连ftp传输文件和我直接使用ftp命令连接服务器传输文件所发出的命令的区别。把抓到的包用wireshark打开,只看协议为ftp的数据(过滤条件:ftp or ftp-data)。按时间顺序看下来,可以看到ftp客户端发送了port命令,使用的是主动模式,客户端在发出LIST请求后,服务端返回了连接失败。

问题原因:

到这里,问题的原因就很明显了:因为我的ftp客户端使用了主动模式(如果你想了解ftp的主动被动模式的区别,可以拉到最后),ftp服务器会主动尝试连接ftp客户端port命令所指定的随机端口建立数据传输连接,但是由于我的ftp客户端与ftp服务器之间还隔着防火墙,ftp客户端的防火墙没有开通(这么多随机端口想必要开防火墙也够喝一壶了),因此就出现了425 Failed to establish connection的返回。

解决办法:

使用主动模式还是被动模式,都是由客户端来决定的。除非你愿意开放一堆端口,不然的话最佳的解决办法就是设置客户端使用被动模式。我用的commons-net包的3.6版本,在初始化ftp客户端的时候就可以直接在代码中设定客户端使用被动模式,代码如下,设置被动模式后,问题解决。

//设置二进制文件传输模式
ftpClient.setFileTransferMode(ftpClient.BINARY_FILE_TYPE);
//设置被动传输模式
ftpClient.enterLocalPassiveMode();

扩展阅读:

ftp主动模式和被动模式:https://www.cnblogs.com/mawanglin2008/articles/3607767.html

0

说点什么

avatar
  Subscribe  
提醒