我正在使用带有Java 8的Apache Commons Net(v3.5)来连接到远程FTPS站点(即在Internet上)。我能够轻松地与Windows 10计算机上的FileZilla客户端连接,但是我的Java程序无法完成相同的步骤。我已经在谷歌上搜索了,但找不到根本原因。这是我已经确认的事情:


我确保Java FTP命令的顺序与FileZilla客户端的顺序完全相同。
我在PC上禁用了Windows防火墙和防病毒
我重新启用了Windows防火墙并启用了日志记录。使用FileZilla时,在建立被动模式连接时,Windows防火墙日志会列出TCP连接。我在Java程序中看不到这样的条目。
我在PC上安装了FileZilla服务器。我取消选中“使用PROT P时要求在数据连接上恢复TLS会话”后,该Java程序正常工作。 Java异常是不同的,因此我不认为这是一支抽烟的枪。
我在test.rebex.com服务器上成功运行了相同的代码。


以下是代码,任何想法都将不胜感激:

import java.io.IOException;
import java.io.PrintWriter;
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.ftp.FTPSClient;

public class testProgram {

  public static void main(String[] args) {

    String ftpServer = "ftp.domain.com";
    String ftpUsername = "user@domain.com";
    String ftpPassword = "********";

    FTPSClient ftp = null;

    // CONNECT TO THE SERVER
    try {
        // I have tried "SSL" as the argument, but same result
        ftp = new FTPSClient();
        ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));

        ftp.connect(ftpServer,21);

        int reply = ftp.getReplyCode();

        if (!FTPReply.isPositiveCompletion(reply)) {
            ftp.disconnect();
            System.err.println("---------->FTP server refused connection.\n");

        }

    } catch (Exception e) {
        System.out.println(e.getMessage());
        e.printStackTrace();

    }

    // LOGIN INTO SERVER
    try {
        if (!ftp.login(ftpUsername, ftpPassword)) {
            ftp.logout();

        } else {

            ftp.sendCommand("OPTS UTF8 ON");
            ftp.execPBSZ(0);
            ftp.execPROT("P");
            ftp.pwd();
            ftp.setFileType(FTP.BINARY_FILE_TYPE);
            ftp.enterLocalPassiveMode();

            /* The next command always fails.

               The FTP Server responds with "150 Accepted data connection" then:

                org.apache.commons.net.ftp.FTPConnectionClosedException: Connection closed without indication.
                at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:316)
                at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:292)
                at org.apache.commons.net.ftp.FTP.getReply(FTP.java:712)
                at org.apache.commons.net.ftp.FTPClient.completePendingCommand(FTPClient.java:1857)
                at org.apache.commons.net.ftp.FTPClient.listNames(FTPClient.java:2919)
                at org.apache.commons.net.ftp.FTPClient.listNames(FTPClient.java:2952)
                at myPackage.testProgram.main(testProgram.java:78)

                I have tried other commands, but it disconnects here...
             */

            FTPFile[] ftpFiles = ftp.listFiles();
            System.out.println("---------->Number of Files = " + ftpFiles.length);
            ftp.logout();

        }
    } catch (Exception e) {

        e.printStackTrace();
    }

    //Ensure Disconnected at the end.
    if (ftp.isConnected()) {
        try {
            ftp.disconnect();
        } catch (IOException f) {
            // do nothing
        }

    }
  }
}


这是我PC上的FileZilla Client日志:

2016-09-06 09:09:50 4756 1 Status: Resolving address of ftp.domain.com
2016-09-06 09:09:51 4756 1 Status: Connecting to h1.h2.h3.h4:21...
2016-09-06 09:09:51 4756 1 Status: Connection established, waiting for welcome message...
2016-09-06 09:09:51 4756 1 Response: 220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------
2016-09-06 09:09:51 4756 1 Response: 220-You are user number 2 of 50 allowed.
2016-09-06 09:09:51 4756 1 Response: 220-Local time is now 13:09. Server port: 21.
2016-09-06 09:09:51 4756 1 Response: 220-This is a private system - No anonymous login
2016-09-06 09:09:51 4756 1 Response: 220-IPv6 connections are also welcome on this server.
2016-09-06 09:09:51 4756 1 Response: 220 You will be disconnected after 15 minutes of inactivity.
2016-09-06 09:09:51 4756 1 Command: AUTH TLS
2016-09-06 09:09:51 4756 1 Response: 234 AUTH TLS OK.
2016-09-06 09:09:51 4756 1 Status: Initializing TLS...
2016-09-06 09:09:51 4756 1 Status: Verifying certificate...
2016-09-06 09:09:51 4756 1 Status: TLS connection established.
2016-09-06 09:09:51 4756 1 Command: USER user@domain.com
2016-09-06 09:09:51 4756 1 Response: 331 User user@domain.com OK. Password required
2016-09-06 09:09:51 4756 1 Command: PASS *************
2016-09-06 09:09:51 4756 1 Response: 230 OK. Current restricted directory is /
2016-09-06 09:09:51 4756 1 Command: SYST
2016-09-06 09:09:51 4756 1 Response: 215 UNIX Type: L8
2016-09-06 09:09:51 4756 1 Command: FEAT
2016-09-06 09:09:51 4756 1 Response: 211-Extensions supported:
2016-09-06 09:09:51 4756 1 Response:  EPRT
2016-09-06 09:09:51 4756 1 Response:  IDLE
2016-09-06 09:09:51 4756 1 Response:  MDTM
2016-09-06 09:09:51 4756 1 Response:  SIZE
2016-09-06 09:09:51 4756 1 Response:  MFMT
2016-09-06 09:09:51 4756 1 Response:  REST STREAM
2016-09-06 09:09:51 4756 1 Response:  MLST type*;size*;sizd*;modify*;UNIX.mode*;UNIX.uid*;UNIX.gid*;unique*;
2016-09-06 09:09:51 4756 1 Response:  MLSD
2016-09-06 09:09:51 4756 1 Response:  AUTH TLS
2016-09-06 09:09:51 4756 1 Response:  PBSZ
2016-09-06 09:09:51 4756 1 Response:  PROT
2016-09-06 09:09:51 4756 1 Response:  UTF8
2016-09-06 09:09:51 4756 1 Response:  TVFS
2016-09-06 09:09:51 4756 1 Response:  ESTA
2016-09-06 09:09:51 4756 1 Response:  PASV
2016-09-06 09:09:51 4756 1 Response:  EPSV
2016-09-06 09:09:51 4756 1 Response:  SPSV
2016-09-06 09:09:51 4756 1 Response:  ESTP
2016-09-06 09:09:51 4756 1 Response: 211 End.
2016-09-06 09:09:51 4756 1 Command: OPTS UTF8 ON
2016-09-06 09:09:51 4756 1 Response: 200 OK, UTF-8 enabled
2016-09-06 09:09:51 4756 1 Command: PBSZ 0
2016-09-06 09:09:51 4756 1 Response: 200 PBSZ=0
2016-09-06 09:09:51 4756 1 Command: PROT P
2016-09-06 09:09:52 4756 1 Response: 200 Data protection level set to "private"
2016-09-06 09:09:52 4756 1 Status: Logged in
2016-09-06 09:09:52 4756 1 Status: Retrieving directory listing...
2016-09-06 09:09:52 4756 1 Command: PWD
2016-09-06 09:09:52 4756 1 Response: 257 "/" is your current location
2016-09-06 09:09:52 4756 1 Command: TYPE I
2016-09-06 09:09:52 4756 1 Response: 200 TYPE is now 8-bit binary
2016-09-06 09:09:52 4756 1 Command: PASV
2016-09-06 09:09:52 4756 1 Response: 227 Entering Passive Mode (h1,h2,h3,h4,133,150)
2016-09-06 09:09:52 4756 1 Command: MLSD
2016-09-06 09:09:52 4756 1 Response: 150 Accepted data connection
2016-09-06 09:09:52 4756 1 Response: 226-Options: -a -l
2016-09-06 09:09:52 4756 1 Response: 226 6 matches total


根据Mike的建议,我打开了TLS调试功能。看来程序再次通过TLS握手。输出非常长,但是发出list命令后,我看到“ *** ClientHello,TLSv1.2”,看起来像与启动FTP连接相同的命令。

差异似乎出现在最后:

%% Cached client session: [Session-2, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
main, received EOFException: ignored
main, called closeInternal(false)
main, SEND TLSv1.2 ALERT:  warning, description = close_notify
main, WRITE: TLSv1.2 Alert, length = 26
main, called closeSocket(false)
main, called close()
main, called closeInternal(true)
main, called close()
main, called closeInternal(true)
main, received EOFException: ignored
main, called closeInternal(false)
main, SEND TLSv1.2 ALERT:  warning, description = close_notify
main, WRITE: TLSv1.2 Alert, length = 26
main, called closeSocket(false)
org.apache.commons.net.ftp.FTPConnectionClosedException: Connection closed without indication.

最佳答案

尽管这看起来像是一篇老文章,但今天我遇到了类似的问题,无法(最初)找到解决方案。我可以通过FileZilla进行连接,但不能与FTPSClient连接,并且在运行ftpClient.enterLocalPassiveMode()之后,我通常会获得425 cannot open data connection

我的解决方案是在登录之前但在连接到FTPServer之后更改ftpClient.enterLocalPassiveMode()并起作用。通常,我看到的所有代码示例在发送/接收数据之前但登录后都使用enterlocalpassivemode。请参阅以下代码,该示例对我有用。

FTPSClient ftpClient = new FTPSClient(false);
ftpClient.connect("remote.ftp.server", port);
ftpClient.enterLocalPassiveMode();// Run the passive mode command now  instead of after loggin in.
ftpClient.login("username", "password");
ftpClient.execPBSZ(0);
ftpClient.execPROT("P");
ftpClient.type(FTP.BINARY_FILE_TYPE);
//ftpClient.enterLocalPassiveMode(); Previously it was here.
FTPFile[] files = ftpClient.listDirectories("/");


希望这可以帮助。还请注意,省略了所有其他代码和良好做法,以使答案简短。

09-10 07:38
查看更多