org.apache.commons.net.ftp.FTPConnectionClosedException: Connection closed without indication


Ftp問題

最近遇到了ftp讀取中文亂碼的問題,代碼中使用的是FtpClient。google一下找到了解決方案。

FTP協議里面,規定文件名編碼為iso-8859-1,FTP類中默認的編碼也是這個。

public FTP() {
    this.setDefaultPort(21);
    this._replyLines = new ArrayList();
    this._newReplyString = false;
    this._replyString = null;
    this._controlEncoding = "ISO-8859-1";
    this._commandSupport_ = new ProtocolCommandSupport(this);
}

參考文章 https://www.cnblogs.com/chenfei0801/p/3427310.html 設置了編碼格式

private FTPClient getClient(ConnectReq connectReq) {
    String host = StringUtils.substringBefore(connectReq.getConnectUrl(), ":");
    int port = Integer.parseInt(StringUtils.substringAfter(connectReq.getConnectUrl(), ":"));

    log.info("Connect to Ftp [{}:{}] with user [{}] and passwd [{}].",
            host, port, ftpReq.getUserName(), ftpReq.decodePassword());
    FTPClient ftpClient = new FTPClient();
    try {
        ftpClient.connect(host, port);
        log.info("Connected.");
        ftpClient.login(ftpReq.getUserName(), ftpReq.decodePassword());
        // Use passive mode to pass firewalls.
        if (FTPReply.isPositiveCompletion(ftpClient.sendCommand(
                "OPTS UTF8", "ON"))) {
            LOCAL_CHARSET = UTF8_CHARSET;
        }
        ftpClient.setControlEncoding(LOCAL_CHARSET);
        ftpClient.enterLocalPassiveMode();
        log.info("Logged.");
        return ftpClient;
    } catch (IOException e) {
        log.error("連接ftp服務-{} 出錯,原因:{}", ftpReq.getHost(), e);
        throw new InnerException(ErrorCode.DSOURCE_ERROR, e);
    }
}

發現程序能夠連接成功,但是在調用client.logout方法時出現了錯誤。

org.apache.commons.net.ftp.FTPConnectionClosedException: Connection closed without indication.
	at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:313)
	at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:290)
	at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:479)
	at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:552)
	at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:601)
	at org.apache.commons.net.ftp.FTP.quit(FTP.java:809)
	at org.apache.commons.net.ftp.FTPClient.logout(FTPClient.java:979)
	...

java.lang.RuntimeException: org.apache.commons.net.ftp.FTPConnectionClosedException: Connection closed without indication.
...
Caused by: org.apache.commons.net.ftp.FTPConnectionClosedException: Connection closed without indication.
	at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:313)
	at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:290)
	at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:479)
	at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:552)
	at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:601)
	at org.apache.commons.net.ftp.FTP.quit(FTP.java:809)
	at org.apache.commons.net.ftp.FTPClient.logout(FTPClient.java:979)
	... 28 more

根據FTPClient官方文檔的解釋

    /***
     * Logout of the FTP server by sending the QUIT command.
     * <p>
     * @return True if successfully completed, false if not.
     * @exception FTPConnectionClosedException
     *      If the FTP server prematurely closes the connection as a result
     *      of the client being idle or some other reason causing the server
     *      to send FTP reply code 421.  This exception may be caught either
     *      as an IOException or independently as itself.
     * @exception IOException  If an I/O error occurs while either sending a
     *      command to the server or receiving a reply from the server.
     ***/
    public boolean logout() throws IOException
    {
        return FTPReply.isPositiveCompletion(quit());
    }

這個錯誤的出現的可能原因是ftp服務器過早關閉或者其他原因導致服務端返回了421

繼續追蹤到后面的異常棧

Connection closed without indication.

這個問題可能是因為FTP服務器服務有故障,或是是網絡問題。於是我立馬用命令行嘗試連接到Ftp上,果然是可以連的,證明ftp服務沒問題。那么就是網絡問題了。

檢查了代碼,在代碼中使用的是被動模式。FTP服務器一般使用20和21兩個端口與客戶端進行通信,21端口用來傳輸FTP的控制命令,20端口用於傳輸文件數據。

如果是主動模式的話,FTP客戶端向服務器的FTP控制端口(默認是21)發送連接請求,服務器接受連接,建立一條命令鏈路;當需要傳送數據時,客戶端在命令鏈路上用PORT的命令告訴服務器我開放了某端口,你過來連接我。於是服務器從20端口向客戶端的該端口發送連接請求,建立一條數據鏈路來傳送數據。在數據鏈路建立過程中是服務器主動請求,所以稱為主動模式。

graph LR subgraph 客戶端 client[客戶端] clientPort[打開端口] client--2.打開-->clientPort end subgraph 服務端 server[服務端] server-->port21 server-->port20 port21(21端口) port20(20端口) client--1.連接請求-->port21 client--3.PORT通知-->server port20--4.建立連接-->clientPort end

如果是被動模式的話,FTP客戶端向服務器的FTP控制端口(默認21)發送連接請求,服務器接受連接,建立一條命令鏈路;當需要傳送數據時,服務器在命令鏈路上用PASV命令告訴客戶端,我打開了某端口,你過來連我。於是客戶端向服務器的該端口發送連接請求,建立一條數據鏈路來傳送數據。在數據鏈路建立的過程中是服務器被動等待客戶機的請求,所以稱被動模式。

graph LR subgraph 客戶端 client[客戶端] clientConnect[建立數據鏈路] client-->clientConnect end subgraph 服務端 server[服務端] server-->port21 server--2.打開-->serverPort port21(21端口) serverPort(打開端口) client--1.連接請求-->port21 server--3.PASV通知-->client clientConnect--4.建立連接-->serverPort end

上文說到代碼中用的是被動模式,也就意味着服務端需要打開21端口和另一個端口供客戶端連接。我使用的ftp是用docker裝的,只透出了21端口,所以使用被動模式連接會出錯。於是把這里改成主動模式,連接成功。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM