为FtpClient设置连接超时时间

9
当使用ftpClient.connect连接到一个没有启用ftp服务的现有主机时,超时时间只有5分钟,这太长了。
我尝试设置不同的超时时间(setDefaultTimeout、setDataTimeout),但没有任何改变。 FtpClient继承自SocketClient,后者具有setConnectTimeout方法,但当我使用它时,运行它时会得到一个java.lang.NoSuchMethodError: org/apache/commons/net/ftp/FTPClient.setConnectTimeout。这似乎是由于某些J2SW 1.2兼容性问题,如Commons-net FAQ所述:
问:我如何设置连接超时? http://wiki.apache.org/commons/Net/FrequentlyAskedQuestions 他们建议实现一个自己的SocketFactory,创建从扩展Socket类中使用特定超时的对象。然而,当我尝试使用ftpClient.setSocketFactory时,我也得到了一个 java.lang.NoSuchMethodError 。
请问如何缩短连接超时时间?

3
FAQ中提到的问题和答案表明:“由于Commons Net 1.2.x需要与J2SE 1.2兼容,因此未包含指定连接超时的功能。” 这意味着Commons Net 1.2.x库没有所需的功能。 如果您不一定需要那个旧版本的库,则可以尝试更高或最新版本的库,例如Commons Net 3.1-其中“FTPClient”类具有“setConnectTimeout”方法,正如下面的答案所述,正好可以满足您的要求。 - Yuriy Nakonechnyy
我使用FTPClient 3.1,在Eclipse中没有显示任何错误,但在应用服务器上部署后,出现了上述错误。Mavne build pom.xml有相同的依赖版本,ear文件包含正确的jar包。 - stracktracer
2
这真的很奇怪,因为这应该可以工作...你尝试从Eclipse下的简单的public static void main(String[] args)方法连接了吗?也许问题在于应用服务器:它可能提供了旧版本的Commons Net库,而您部署的应用程序可能会从那里使用FTPClient... - Yuriy Nakonechnyy
1
是的,这个可以工作。它正在Weblogic 10.3.5中运行。也许在类路径中有一个更旧的commons-net被Weblogic自身包含,并具有优先级? - stracktracer
4个回答

11
    FTPClient ftp = new FTPClient();

    ftp.setDefaultTimeout();
    ftp.setDataTimeout();
    ftp.setConnectTimeout();
    ftp.setSoTimeout();
    ftp.setControlKeepAliveTimeout();
    ftp.setControlKeepAliveReplyTimeout();

来自Apache文档:

   /**
     * Set the default timeout in milliseconds to use when opening a socket.
     * This value is only used previous to a call to
     * {@link #connect connect()}
     * and should not be confused with {@link #setSoTimeout setSoTimeout()}
     * which operates on an the currently opened socket.  _timeout_ contains
     * the new timeout value.
     * <p>
     * @param timeout  The timeout in milliseconds to use for the socket
     *                 connection.
     */
    void setDefaultTimeout(int timeout);


    /**
     * Sets the timeout in milliseconds to use when reading from the
     * data connection.  This timeout will be set immediately after
     * opening the data connection, provided that the value is &ge; 0.
     * <p>
     * <b>Note:</b> the timeout will also be applied when calling accept()
     * whilst establishing an active local data connection.
     * @param  timeout The default timeout in milliseconds that is used when
     *        opening a data connection socket. The value 0 means an infinite timeout.
     */
    void setDataTimeout(int timeout)
    /**
     * Sets the connection timeout in milliseconds, which will be passed to the {@link java.net.Socket} object's
     * connect() method.
     * @param connectTimeout The connection timeout to use (in ms)
     * @since 2.0
     */
    void setConnectTimeout(int connectTimeout);
    /**
     * Set the timeout in milliseconds of a currently open connection.
     * Only call this method after a connection has been opened
     * by {@link #connect connect()}.
     * <p>
     * To set the initial timeout, use {@link #setDefaultTimeout(int)} instead.
     *
     * @param timeout  The timeout in milliseconds to use for the currently
     *                 open socket connection.
     * @exception SocketException If the operation fails.
     * @throws NullPointerException if the socket is not currently open
     */
    void setSoTimeout(int timeout) throws SocketException;
    /**
     * Set the time to wait between sending control connection keepalive messages
     * when processing file upload or download.
     *
     * @param controlIdle the wait (in secs) between keepalive messages. Zero (or less) disables.
     * @since 3.0
     * @see #setControlKeepAliveReplyTimeout(int)
     */
    void setControlKeepAliveTimeout(long controlIdle);

    /**
     * Set how long to wait for control keep-alive message replies.
     *
     * @param timeout number of milliseconds to wait (defaults to 1000)
     * @since 3.0
     * @see #setControlKeepAliveTimeout(long)
     */
    void setControlKeepAliveReplyTimeout(int timeout)

4

可能是你调用setConnectTimeout的方式有问题,因为它确实存在。setConnectTimeout不是静态调用,你必须在分配FTPClient对象后进行调用,并在连接之前进行设置。

FTPClient ftp = new FTPClient();
ftp.setConnectTimeout(5000); // 5000 Milliseconds (5 Seconds)
... 
ftp.connect(server, port); 

那应该是你的设置问题。我正在使用commons-net-3.0.1.jar,而且我已经在Eclipse中尝试过了。你可能要检查并确保没有优先加载旧版本的jar文件。 - Mike

3

虽然旧版本的Commons Net库有可能有解决方案,但我建议先找出为什么会使用错误的版本。为此,您可以将以下代码添加到Web应用程序中使用FTPClient的位置:

FTPClient ftpClient = ...;
if(ftpClient.getClass().getClassLoader() instanceof java.net.URLClassLoader) {
    URL[] urls = ((java.net.URLClassLoader) ftpClient.getClass().getClassLoader()).getURLs();
    // log these urls somewhere and check - these are urls from where your FTPClient may be loaded from
}

如果FTPClient不是由java.net.URLClassLoader加载,则检查类路径可能会更加复杂,但这应该不是问题。

希望这可以帮到您...


该数组包含14个条目:weblogic/patch_wls1032/profiles/default/sys_manifest_classpath/weblogic_patch.jar、weblogic/jrmc-4.0.1-1.6.0/lib/tools.jar、weblogic/utils/config/10.3/config-launch.jar、weblogic/wlserver_10.3/server/lib/weblogic_sp.jar、weblogic/wlserver_10.3/server/lib/weblogic.jar,...不包括我在ear部署中捆绑的commons-net库。 - stracktracer
2
嗯,很奇怪。我从未使用过weblogic,但以下问题/答案似乎接近您所需的内容: https://dev59.com/RE_Ta4cB1Zd3GeqPA3RU - Yuriy Nakonechnyy
确实,需要进行一些特定的工作来设置类加载器中冲突类的优先级,以便首先加载捆绑的类。 - Alfabravo

2

为了对这些值进行澄清(经过手动测试许多组合后,需要提到这与Spring DefaultFtpSessionFactory 使用底层的Apache FTPClient 相关 - 因此最终适用于该客户端):

import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;

DefaultFtpSessionFactory factory = new DefaultFtpSessionFactory();

/**
 * Controls 2 values:
 * 1. protected abstract void connect(SocketAddress address, int timeout) throws IOException;
 * --> controls timeout when opening socket connection. this one is by default 0 in java-code,
 * but since OS is controlling this it has some default value - for me in win.
 * it was ~20 seconds. If set to lower value it will be respected. If set >20 in my case,
 * it always treats it as if 20 was set
 * 2. _socket_.setSoTimeout(connectTimeout);
 * --> controls timeout after socket is open (so if FTP server is not responding after
 * socket is successfully connected). Default is unlimited so good to set to some sane value
 * otherwise if no response from FTP Server, connection will hang. Overwrites setDefaultTimeout
 * if already set - but only for this connection time (not after FTP server responds first time
 * with some data).
 */
factory.setConnectTimeout((int) Duration.ofMinutes(1).toMillis());

/**
 * Controls timeout after socket is open (so if FTP server is not responding after socket is
 * successfully connected).(IF NOT ALREADY SET IN setConnectTimeout),
 * but also controls timeout when reading data from socket after the connection has been made.
 * So if FTP client sends "LIST /" command, and there is no answer from FTP server, without
 * setting this it will hang (since default is 0). Set to some sane value
 * (since server can actually be busy with creating listing of folders for longer time etc.).
 */
factory.setDefaultTimeout((int) Duration.ofMinutes(1).toMillis());

/**
 * Controls how long to wait if there is a socket inactivity during FILE-related operations.
 * E.g. if we start to download some file from FTP server, this timeout is respected.
 * This is by default set to 0 (that is infinite wait). If set to 10 seconds, and there is at
 * least some activity in communication (eg. every 9 seconds something is received) then there
 * will be no timeout. Only if there is some delay/inactivity for longer than 10 seconds then
 * there will be socketRead0 timeout exception. Should be set, since this is not affected by
 * setConnectTimeout or setDefaultTimeout.
 */
factory.setDataTimeout((int) Duration.ofMinutes(1).toMillis());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接