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端口,所以使用被动模式连接会出错。于是把这里改成主动模式,连接成功。

posted @ 2019-12-19 14:56  JacobZheng  阅读(8562)  评论(0编辑  收藏  举报