java ftp keepalive_记录FTPClient超时处理的相关问题(转)

本文探讨了使用Apache Commons Net FTPClient在上传文件时遇到的超时问题。在网络限速情况下,尽管设置了超时,FTPClient的文件上传仍然会卡住。通过分析FTPClient源码,揭示了connectTimeout、setDefaultTimeout、setSoTimeout、setDataTimeout等超时设置的用途。在文件上传场景中,setDataTimeout超时设置无效,原因是它仅针对Socket输入流的读取操作。解决方案是业务层自定义超时处理。文章还涵盖了其他FTPClient在文件上传和下载过程中的常见异常及其原因。
摘要由CSDN通过智能技术生成

apache 有个开源库:commons-net,这个开源库中包括了各种基础的网络工具类,我使用了这个开源库中的 FTP 工具。

但碰到一些问题,并不是说是开源库的 bug,可能锅得算在产品头上吧,各种奇怪需求。

问题

当将网络限速成 1KB/S 时,使用 commons-net 开源库中的 FTPClient 上传本地文件到 FTP 服务器上,FTPClient 源码内部是通过 Socket 来实现传输的,当终端和服务器建立了连接,调用 storeFile() 开始上传文件时,由于网络限速问题,一直没有接收到是否传输结束的反馈,导致此时,当前线程一直卡在 storeFile(),后续代码一直无法执行。

如果这个时候去 FTP 服务器上查看一下,会发现,新创建了一个 0KB 的文件,但本地文件中的数据内容就是没有上传上来。

产品要求,需要有个超时处理,比如上传工作超过了 30s 就当做上传失败,超时处理。但我明明调用了 FTPClient 的相关超时设置接口,就是没有一个会生效。

一句话简述下上述的场景问题:

网络限速时,为何 FTPClient 设置了超时时间,但文件上传过程中超时机制却一直没生效?

一气之下,干脆跟进 FTPClient 源码内部,看看为何设置的超时失效了,没有起作用。

所以,本篇也就是梳理下 FTPClient 中相关超时接口的含义,以及如何处理上述场景中的超时功能。

源码跟进

先来讲讲对 FTPClient 的浅入学习过程吧,如果不感兴趣,直接跳过该节,看后续小节的结论就可以了。

ps:本篇所使用的 commons-net 开源库版本为 3.6

使用

首先,先来看看,使用 FTPClient 上传文件到 FTP 服务器大概需要哪些步骤:

//1.与 FTP 服务器创建连接

ftpClient.connect(hostUrl, port);

//2.登录

ftpClient.login(username, password);

//3.进入到指定的上传目录中

ftpClient.makeDirectory(remotePath);

ftpClient.changeWorkingDirectory(remotePath);

//4.开始上传文件到FTP

ftpClient.storeFile(file.getName(), fis);

当然,中间省略其他的配置项,比如设置主动模式、被动模式,设置每次读取本地文件的缓冲大小,设置文件类型,设置超时等等。但大体上,使用 FTPClient 来上传文件到 FTP 服务器的步骤就是这么几个。

既然本篇主要是想理清超时为何没生效,那么也就先来看看都有哪些设置超时的接口:

4c1076c54ffd998fee7d6e241ec9a7e5.png

粗体字是 FTPClient 类中提供的方法,而 FTPClient 的继承关系如下:

FTPClient extends FTP extends SocketClient

非粗体字的方法都是 SocketClient 中提供的方法。

好,先清楚有这么几个设置超时的接口存在,后面再从跟进源码过程中,一个个来了解它们。

跟进

1. connect()

那么,就先看看第一步的 connect():

//SocketClient#connect()

public void connect(String hostname, int port) throws SocketException, IOException {

_hostname_ = hostname;

_connect(InetAddress.getByName(hostname), port, null, -1);

}

//SocketClient#_connect()

private void _connect(InetAddress host, int port, InetAddress localAddr, int localPort) throws SocketException, IOException {

//1.创建socket

_socket_ = _socketFactory_.createSocket();

//2.设置发送窗口和接收窗口的缓冲大小

if (receiveBufferSize != -1) {

_socket_.setReceiveBufferSize(receiveBufferSize);

}

if (sendBufferSize != -1) {

_socket_.setSendBufferSize(sendBufferSize);

}

//3.socket(套接字:ip 和 port 组成)

if (localAddr != null) {

_socket_.bind(new InetSocketAddress(localAddr, localPort));

}

//4.连接,这里出现 connectTimeout 了

_socket_.connect(new InetSocketAddress(host, port), connectTimeout);

_connectAction_();

}

所以, FTPClient 调用的 connect() 方法其实是调用父类的方法,这个过程会去创建客户端 Socket,并和指定的服务端的 ip 和 port 创建连接,这个过程中,出现了一个 connectTimeout,与之对应的 FTPClient 的超时接口:

//SocketClient#setConnectTimeout()

public void setConnectTimeout(int connectTimeout) {

this.connectTimeout = connectTimeout;

}

至于内部是如何创建计时器,并在超时后是如何抛出 SocketTimeoutException 异常的,就不跟进了,有兴趣自行去看,这里就看一下接口的注释:

/**

* Connects this socket to the server with a specified timeout value.

* A timeout of zero is interpreted as an infinite timeout. The connection

* will then block until established or an error occurs.

* (用该 socket 与服务端创建连接,并设置一个指定的超时时间,如果超时时间是0,表示超时时间为无穷大,

* 创建连接这个过程会进入阻塞状态,直到连接创建成功,或者发生某个异常错误)

* @param endpoint the {@code SocketAddress}

* @param timeout the timeout value to be used in milliseconds.

* @throws IOException if an error occurs during the connection

* @throws SocketTimeoutException if timeout expires before connecting

* @throws java.nio.channels.IllegalBlockingModeException

* if this socket has an associated channel,

* and the channel is in non-blocking mode

* @throws IllegalArgumentException if endpoint is null or is a

* SocketAddress subclass not supported by this socket

* @since 1.4

* @spec JSR-51

*/

public void connect(SocketAddress endpoint, int timeout) throws IOException

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值