apache commons FTPSClient.listFiles()触发java.net.ConnectException:连接被拒绝:连接
我用Java编写了一个程序,连接到FTPS服务器,在某个父目录中循环查找名称与给定regexp匹配的文件,然后将这些文件检索到本地计算机。我正在使用ApacheCommonsNet3.6和Java8。下面是我如何连接并访问需要迭代的文件夹列表。fileType是字符串输入参数,可以为null。fileNameRegExp也是一个字符串输入参数,其中包含的regexp将仅过滤具有正确命名的文件:apache commons FTPSClient.listFiles()触发java.net.ConnectException:连接被拒绝:连接,java,ftps,apache-commons-net,Java,Ftps,Apache Commons Net,我用Java编写了一个程序,连接到FTPS服务器,在某个父目录中循环查找名称与给定regexp匹配的文件,然后将这些文件检索到本地计算机。我正在使用ApacheCommonsNet3.6和Java8。下面是我如何连接并访问需要迭代的文件夹列表。fileType是字符串输入参数,可以为null。fileNameRegExp也是一个字符串输入参数,其中包含的regexp将仅过滤具有正确命名的文件: String server = "<here I type the dot-separ
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){
}
到目前为止还不错。文件被读取并通过套接字复制到本地文件夹。现在,当代码找到另一个文件时,我遇到了麻烦。调用sftpClient.listFiles()返回空。如果出于调试目的,我尝试检查返回的数组是否为空,并尝试第二次调用sftpClient.listFiles(),则会得到一个异常:java.net.ConnectException:Connection拒绝:connect
有趣的是,就在这之前,我调用了sftpClient.isConnected()和sftpClient.isAvailable(),两者都返回true
在这一点上,我所关心的是创建代码列表并传输第二个文件,所以我更改代码以在流到达第二个文件的子文件夹时转移流。在新的流程中,我注销、断开连接,再次执行所有FTPSClient设置,因为断开连接会重置对象中的一些内容,重新连接、再次登录和预启动。它读取并传输文件。当然,这是个骗局。我知道它在哪里失败,设置了一个带断开和重新连接的绕道,所以这不是解决方案。在这里,我完全处于黑暗之中,没有想法。我运行了我知道的检查,以确保套接字仍然可用并工作,并且程序仍然连接到远程服务器,但仍然失败。连接/套接字似乎有问题,但除非有一个测试我可以运行,这样我就知道它过时了,否则我永远不知道什么时候需要断开/重新连接程序才能继续
正如最后一句话,我必须检查FTPS服务器的唯一方法是通过FileZilla连接,使用完全相同的用户名、密码、服务器ip和端口,这样可以很好地工作。它列出了/main_文件夹中的目录,我可以查看和重命名树中的所有文件,并可以来回传输文件。我无法使用控制台连接到它,也无法通过cmd或bash调用sftp
我欢迎您提出任何关于如何解决此问题或获得更多关于此bug的见解
提前谢谢你,并致以亲切的问候
更新:
我想感谢@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(), String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT);
method.invoke(cache, key, 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命令的新数据连接(调用父类中的受保护套接字\u openDataConnection(String命令,String arg)
,这会触发对FTP类中的public int pasv()
的调用)。当目录中没有匹配的文件时,代码会发出CWD、PASV和列表。当有文件匹配时,它是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
,并且私有FTPListParseEngine initiateListParsing(FTPFileEntryParser解析器,字符串路径名)
返回一个FTPListParseEngine对象,该对象不读取当前工作目录中的文件服务器列表。这就是listFiles()返回空FTPFile数组的原因
我尝试重写sendCommand以获得一个带有227的PASV(在上一个PASV之后发出一个新的PASV),但是当代码尝试连接到套接字时,我得到了java.net.ConnectException:连接被拒绝,几乎就像服务器没有侦听该端口一样,但是是服务器返回了数据套接字的套接字详细信息,对吗?我的意思是,这是PASV的结果:服务器告诉客户端去哪里检索数据。为什么它会拒绝连接
我希望这对某些人来说是有意义的,因为一个250帕斯卡的车没有意义。我在这里迷路了o/试试这个。覆盖_prepareDataSocket u()。谢谢你,@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(), String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT);
method.invoke(cache, key, 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());
}
}
}
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;
}
}