Java Apache Commons FTP被动模式,如何设置远程侦听端口(数据流)

Java Apache Commons FTP被动模式,如何设置远程侦听端口(数据流),java,ftp,apache-commons-net,Java,Ftp,Apache Commons Net,我尝试使用apache-commons-net-3.7.2(隐式TLS,使用客户端证书+登录/密码的双因素身份验证)连接FTP服务器 我可以对自己进行身份验证,进入被动模式,但客户端无法成功连接到服务器以通过数据套接字获取数据 我可以在同一台计算机上用WinSCP(相同的设置)连接自己。我已激活WinSCP日志以查看协议详细信息,并使用相同的选项调整了源代码。我可以使用协议CommandListener验证我的协议是否正常。我知道需要被动模式,因为WinSCP发出PASV命令 我可以看到WinS

我尝试使用apache-commons-net-3.7.2(隐式TLS,使用客户端证书+登录/密码的双因素身份验证)连接FTP服务器

我可以对自己进行身份验证,进入被动模式,但客户端无法成功连接到服务器以通过数据套接字获取数据

我可以在同一台计算机上用WinSCP(相同的设置)连接自己。我已激活WinSCP日志以查看协议详细信息,并使用相同的选项调整了源代码。我可以使用
协议CommandListener
验证我的协议是否正常。我知道需要被动模式,因为WinSCP发出
PASV
命令

我可以看到WinSCP连接到端口62564上的数据套接字(我已将FTP IP地址替换为XXX)

2021-01-06 10:25:35.575 227进入被动模式(192168,4122244100)。
2021-01-06 10:25:35.575服务器发送了带有不可中断地址192.168.4.122的被动回复,改为使用主机地址。
2021-01-06 10:25:35.575百万标准偏差
2021-01-06 10:25:35.575连接83.XXX.XXX.XXX:62564。。。
2021-01-06 10:25:35.604 150传送目录
我还可以看到服务器为
PASV
命令发送的回复不包括要连接的端口

public class TestApi {
    
    public static void _parseExtendedPassiveModeReply(String reply)
        {
            reply = reply.substring(reply.indexOf('(') + 1,
                    reply.indexOf(')')).trim();
    
            char delim1, delim2, delim3, delim4;
            delim1 = reply.charAt(0);
            delim2 = reply.charAt(1);
            delim3 = reply.charAt(2);
            delim4 = reply.charAt(reply.length()-1);
    
            if (!(delim1 == delim2) || !(delim2 == delim3)
                    || !(delim3 == delim4)) {
                System.out.println("Could not parse extended passive host information.\nServer Reply: " + reply);
            }
    
            int port;
            try
            {
                port = Integer.parseInt(reply.substring(3, reply.length()-1));
            }
            catch (NumberFormatException e)
            {
                System.out.println("Could not parse extended passive host information.\nServer Reply: " + reply);
            }
        }   
    
    public static void main(String[] args) throws SocketException, IOException, GeneralSecurityException {

        String hostname = args[0];
        int port = Integer.parseInt(args[1]);
        String login = args[2];
        String pwd = args[3];

        FTPSClient client = new FTPSClient("TLS",true);
        
        File clientCertStore = new File("myJCEKS keystore");        
        KeyManager keyManager = KeyManagerUtils.createClientKeyManager("JCEKS",clientCertStore,"","myalias","");        
        client.setKeyManager(keyManager);
        
        client.connect(hostname, port);
        int reply = client.getReplyCode();

        if (!FTPReply.isPositiveCompletion(reply)) {
            client.disconnect();
            System.err.println("FTP server refused connection.");
            System.exit(1);
        } else {
            if (client.login(login, pwd)) {
                
                client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.err), true));
                client.sendCommand("OPTS","UTF8 ON");
                client.sendCommand("PBSZ","0");
                client.sendCommand("PROT","P");
                
                int retour = client.pasv();
                System.out.println(retour);
                _parseExtendedPassiveModeReply(client.getReplyString());                
                
                System.out.println(client.printWorkingDirectory());
                reply = client.getReplyCode();
                System.out.println(reply);
                
                System.out.println(client.listHelp());
                
                //it freezes here, after sending MLDS command 
                //same thing using regular api for listing files (WinSCP use MLSD while regular api uses LIST)      
                client.sendCommand("MLSD");

                //and so on

                System.out.println("LOGOUT");
                client.logout();
            } else {
                System.out.println("echec login");
            }
        }

    }
}
我假设API不知道必须在哪个端口上发送数据请求,并且使用了一个不正常的默认值。我不知道WinSCP是如何成功计算62564端口号的

选择打开UTF8
200命令选择成功
PBSZ 0
200 PBSZ=0
保护蛋白
设置200个专用数据通道保护级别
PASV
227进入被动模式(192168,4122245,74)。
227
PWD
无法分析扩展的被动主机信息。
服务器回复:192168,4122245,74
无法分析扩展的被动主机信息。
服务器回复:192168,4122245,74
257“/”是当前目录
/
257
帮助
214执行以下命令
ABRO ACCT ALLO*APPE CDUP CWD DELE专长+帮助
主机+语言+列表MDTM+MLST+MKD模式NLST NOOP
选择+通过PASV端口PWD退出REIN REST RETR
RMD RNFR RNTO站点大小SMNT STAT STOU
STRU*系统类型用户XCUP XCRC XCWD XMD5 XMKD
XPWD XRMD XSIGN XSHA1 XSHA256 XSHA512 XQUOTA
214帮助完成
214执行以下命令
ABRO ACCT ALLO*APPE CDUP CWD DELE专长+帮助
主机+语言+列表MDTM+MLST+MKD模式NLST NOOP
选择+通过PASV端口PWD退出REIN REST RETR
RMD RNFR RNTO站点大小SMNT STAT STOU
STRU*系统类型用户XCUP XCRC XCWD XMD5 XMKD
XPWD XRMD XSIGN XSHA1 XSHA256 XSHA512 XQUOTA
214帮助完成
MLSD

在API文档、源代码、FTP RFC中搜索了几个小时后,我不知道该怎么做。

你的假设是错误的。您没有设置端口。服务器会告诉您要连接到哪个端口

public class TestApi {
    
    public static void _parseExtendedPassiveModeReply(String reply)
        {
            reply = reply.substring(reply.indexOf('(') + 1,
                    reply.indexOf(')')).trim();
    
            char delim1, delim2, delim3, delim4;
            delim1 = reply.charAt(0);
            delim2 = reply.charAt(1);
            delim3 = reply.charAt(2);
            delim4 = reply.charAt(reply.length()-1);
    
            if (!(delim1 == delim2) || !(delim2 == delim3)
                    || !(delim3 == delim4)) {
                System.out.println("Could not parse extended passive host information.\nServer Reply: " + reply);
            }
    
            int port;
            try
            {
                port = Integer.parseInt(reply.substring(3, reply.length()-1));
            }
            catch (NumberFormatException e)
            {
                System.out.println("Could not parse extended passive host information.\nServer Reply: " + reply);
            }
        }   
    
    public static void main(String[] args) throws SocketException, IOException, GeneralSecurityException {

        String hostname = args[0];
        int port = Integer.parseInt(args[1]);
        String login = args[2];
        String pwd = args[3];

        FTPSClient client = new FTPSClient("TLS",true);
        
        File clientCertStore = new File("myJCEKS keystore");        
        KeyManager keyManager = KeyManagerUtils.createClientKeyManager("JCEKS",clientCertStore,"","myalias","");        
        client.setKeyManager(keyManager);
        
        client.connect(hostname, port);
        int reply = client.getReplyCode();

        if (!FTPReply.isPositiveCompletion(reply)) {
            client.disconnect();
            System.err.println("FTP server refused connection.");
            System.exit(1);
        } else {
            if (client.login(login, pwd)) {
                
                client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.err), true));
                client.sendCommand("OPTS","UTF8 ON");
                client.sendCommand("PBSZ","0");
                client.sendCommand("PROT","P");
                
                int retour = client.pasv();
                System.out.println(retour);
                _parseExtendedPassiveModeReply(client.getReplyString());                
                
                System.out.println(client.printWorkingDirectory());
                reply = client.getReplyCode();
                System.out.println(reply);
                
                System.out.println(client.listHelp());
                
                //it freezes here, after sending MLDS command 
                //same thing using regular api for listing files (WinSCP use MLSD while regular api uses LIST)      
                client.sendCommand("MLSD");

                //and so on

                System.out.println("LOGOUT");
                client.logout();
            } else {
                System.out.println("echec login");
            }
        }

    }
}
对于WinSCP:

2021-01-06 10:25:35.575 227进入被动模式(192168,4122,244100)。

2021-01-06 10:25:35.575连接83.XXX.XXX.XXX:62564

其中62564=(244