如何解决apache commons FTPSClient.listFiles触发java.net.ConnectException:连接被拒绝:connect
我用Java编写了一个程序,该程序连接到FTPS服务器,循环访问某个父目录中的所有目录,以查找名称与给定regexp匹配的文件,然后将这些文件检索到本地计算机。我正在使用Apache Commons Net 3.6和Java8。这是我连接并进入需要迭代的文件夹列表的方式。 fileType是String输入参数,可以为null。 fileNameRegExp还是一个String输入参数,它包含正则表达式,该正则表达式将仅过滤具有正确命名的文件:
String server = "<here I type the dot-separated IP address for the FTPS server>";
int port = <port number>;
String user = "username";
String pass = "********";
String folder = "C:\\Target\\InputFiles\\";
String protocol = "TLS";
int timeoutInMillis = 30000;
FTPSClient sftpClient = new FTPSClient(protocol,true);
int reply ;
try {
sftpClient.setDataTimeout(timeoutInMillis);
sftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
FTPClientConfig clientConfig = new FTPClientConfig(FTPClientConfig.SYST_NT);
sftpClient.configure(clientConfig);
sftpClient.connect(server,port);
sftpClient.setKeepAlive(Boolean.TRUE);
sftpClient.login(user,pass);
reply = sftpClient.getReplyCode();
System.out.println("INFO: ftp login status = <"+reply+">.");
sftpClient.enterLocalPassiveMode();
sftpClient.setFileType(FTP.BINARY_FILE_TYPE);
sftpClient.execPBSZ(0);
sftpClient.execPROT("P");
sftpClient.changeWorkingDirectory("/main_folder");
reply = sftpClient.getReplyCode();
System.out.println("INFO: ftp CWD command on /main_folder status = <"+reply+">.");
List<String> ftpDirectories = new ArrayList<>();
if( fileType!= null && !fileType.isEmpty() ) {
ftpDirectories.add("/main_folder/"+fileType);
} else {
FTPFile[] ftpDirList = sftpClient.listDirectories();
for( FTPFile dir : ftpDirList)
ftpDirectories.add("/main_folder/"+dir.getName());
}
for( String workdirectory : ftpDirectories) {
sftpClient.changeWorkingDirectory(workdirectory);
FTPFile[] ftpFiles = Arrays.stream(sftpClient.listFiles())
.filter(file -> file.getName().matches(fileNameRegExp))
.toArray(FTPFile[]::new);
for (FTPFile ftpFile : ftpFiles) {
(...here's the file transfer logic for each FTPFile found...)
}
}
}catch(Exception ex){
}
大多数文件夹都没有匹配的文件,这是可以预期的,但是可能是我在/ main_folder的几个或所有子文件夹中找到了一堆。为了进行测试,我添加了2个匹配的文件,每个文件都位于不同的子文件夹中。这是测试运行到控制台的示例输出:
220 Microsoft FTP Service
USER username
331 Password required
PASS ********
230 User logged in.
INFO: ftp login status = <230>.
TYPE I
200 Type set to I.
PBSZ 0
200 PBSZ command successful.
PROT P
200 PROT command successful.
CWD /main_folder
250 CWD command successful.
INFO: ftp CWD command on /main_folder status = <250>.
PASV
227 Entering Passive Mode (GG,XX,TT,VV,N,LL).
[Replacing site local address XX.XX.XX.XX with YY.YY.YY.YY]
LIST
125 Data connection already open; Transfer starting.
226 Transfer complete.
CWD /main_folder/subfolder_1
250 CWD command successful.
PASV
227 Entering Passive Mode (GG,Z,KK).
[Replacing site local address XX.XX.XX.XX with YY.YY.YY.YY]
LIST
125 Data connection already open; Transfer starting.
226 Transfer complete.
CWD /main_folder/subfolder_2
250 CWD command successful.
CWD /main_folder/subfolder_winner_1
250 CWD command successful.
PASV
227 Entering Passive Mode (GG,W,RR).
[Replacing site local address XX.XX.XX.XX with YY.YY.YY.YY]
LIST
125 Data connection already open; Transfer starting.
226 Transfer complete.
PASV
227 Entering Passive Mode (GG,M,OO).
[Replacing site local address XX.XX.XX.XX with YY.YY.YY.YY]
RETR matching_file_1.txt
125 Data connection already open; Transfer starting.
到目前为止一切顺利。将读取文件,并通过套接字将其复制到本地文件夹。现在,当代码找到另一个文件时,我遇到了麻烦。 sftpClient.listFiles()的调用返回空。如果出于调试目的,我尝试检查返回的数组是否为空,并尝试运行对sftpClient.listFiles()的第二次调用,则会出现异常:java.net.ConnectException:连接被拒绝:connect
有趣的是,在我调用sftpClient.isConnected()和sftpClient.isAvailable()之前,它们都返回true
在这一点上,我所关心的只是编写代码列表并无论如何传输第二个文件,因此我更改代码以在第二个文件到达该子文件夹时转移流。在新流程中,我注销,断开连接,并再次执行所有FTPSClient设置,因为断开连接会重置对象中的某些内容,然后重新连接,再次登录并进行重新存储。它读取并传输文件。当然是骗人的。我知道哪里出了故障,绕开了连接和重新连接的弯路,所以这不是解决方案。在这里,我完全处于黑暗之中,没有想法。我运行了我知道的检查,以确保套接字仍然可用并且可以正常工作,并且程序仍连接到远程服务器并且仍然失败。连接/套接字似乎确实存在问题,但是除非可以进行测试,否则我知道它已经过时,所以我永远不知道何时需要断开/重新连接程序才能继续。
最后一句话,我必须在FTPS服务器上检查的唯一方法是使用完全相同的用户名,passwd,服务器ip和端口通过FileZilla连接,并且这种方式可以正常工作。它列出了/ main_folder中的目录,我可以看到并重命名树中的所有文件,并且可以来回传输文件。我无法使用控制台连接到它,也无法通过cmd或bash调用sftp。
我欢迎您提出任何有关如何解决此问题或对错误进行更多了解的想法。
先谢谢您的问候
更新 :
我要感谢@AllinProgram的建议,但不幸的是结果是一样的。这是我的替代:
@Override
protected void _prepareDataSocket_(final Socket socket) throws IOException {
if(socket instanceof SSLSocket) {
// Control socket is SSL
final SSLSession session = ((SSLSocket) _socket_).getSession();
final SSLSessionContext context = session.getSessionContext();
//context.setSessionCacheSize(0);
try {
final Field sessionHostPortCache = context.getClass().getDeclaredField("sessionHostPortCache");
sessionHostPortCache.setAccessible(true);
final Object cache = sessionHostPortCache.get(context);
final Method method = cache.getClass().getDeclaredMethod("put",Object.class,Object.class);
method.setAccessible(true);
String key = String.format("%s:%s",socket.getInetAddress().getHostName(),String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT);
method.invoke(cache,key,session);
key = String.format("%s:%s",socket.getInetAddress().getHostAddress(),session);
}
catch(NoSuchFieldException e) {
// Not running in expected JRE
System.out.println("No field sessionHostPortCache in SSLSessionContext. Exception: " + e);
}
catch(Exception e) {
// Not running in expected JRE
System.out.println(e.getMessage());
}
}
}
在主方法类中,我调用System.setProperty("jdk.tls.useExtendedMasterSecret","false");
经过进一步调试后,我注意到了一件事。 listFiles()实际上触发LIST命令的新数据连接(在父类中调用protected Socket _openDataConnection_(String command,String arg)
,在ftp类中触发对public int pasv()
的调用)。当目录中没有匹配的文件时,代码将发出CWD,PASV和LIST。当有文件匹配时,它是CWD,PASV,LIST,PASV,RETR。然后,对于随后的目录,当触发listFiles()时,PASV命令返回250答复。此时,最后发出的命令是CWD,也带有250答复。发出FTP命令的方法如下:
public int sendCommand(String command,String args) throws IOException {
if (this._controlOutput_ == null) {
throw new IOException("Connection is not open");
} else {
String message = this.__buildMessage(command,args);
this.__send(message);
this.fireCommandSent(command,message);
this.__getReply();
return this._replyCode;
}
}
PASV应该返回227,但是此调用使我得到250。这几乎就像是在运行,但是应答代码卡在CWD应答代码中,后者为250。这导致 openDataConnection 返回套接字的null
,而private FTPListParseEngine initiateListParsing(FTPFileEntryParser parser,String pathname)
返回一个FTPListParseEngine对象,该对象不读取当前工作目录中文件的服务器列表。这就是为什么listFiles()返回一个空的FTPFile数组的原因。
我尝试覆盖sendCommand以获取带有227的PASV(在上一个之后立即发出新的PASV),但是当代码尝试连接到套接字时,我得到了java.net.ConnectException:连接被拒绝,几乎好像服务器没有在侦听该端口,但正是服务器返回了数据套接字的那些套接字详细信息,对吗?我的意思是,这是PASV的结果:服务器告诉客户端要去哪里检索数据。为什么会拒绝连接?
我希望这对某人有意义,因为250 PASV没有。我在这里很迷路。 :o /
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。