Apache Commons Net 3.13.0 配置参数详解

用探针程序实测 Apache Commons Net 3.13.0 各客户端的默认参数,逐项给出修改方式、生效验证与生产模板,覆盖超时、字符集、传输模式与列表解析器

Apache Commons NetApache Commons Net2026/10/15

Apache Commons Net 3.13.0 配置参数详解

目标:把 Commons Net 3.13.0 每个客户端的默认参数"量"出来,再逐项演示修改方式与生效验证,最终给出可直接复用的生产参数模板。已在 Ubuntu 24.04.5 LTS(x86_64)+ OpenJDK 17.0.20.1 实测通过。

一、环境信息

  • OS:Ubuntu 24.04.5 LTS
  • 架构:x86_64
  • JDK:OpenJDK 17.0.20.1
  • 构建工具:Apache Maven 3.9.16
  • 组件版本:Apache Commons Net 3.13.0(传递依赖 commons-io 2.21.0)
  • Maven 坐标:commons-net:commons-net:3.13.0
  • 工作目录:/root/commons-net-demo(与《快速入门与集成示例》共用同一验证工程)

Commons Net 没有配置文件,全部参数都是运行时 API。本文所有"默认值"均为新建客户端实例后的实测读数,不是文档转述。

二、参数查看与修改方式

2.1 探针程序实测默认值

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ntp.NTPUDPClient;
import org.apache.commons.net.pop3.POP3Client;
import org.apache.commons.net.smtp.SMTPClient;
import org.apache.commons.net.telnet.TelnetClient;
import org.apache.commons.net.tftp.TFTPClient;

public class ConfigProbe {
    public static void main(String[] args) throws Exception {
        FTPClient ftp = new FTPClient();
        System.out.println("== FTPClient 默认值 ==");
        System.out.println("getDefaultPort          = " + ftp.getDefaultPort());
        System.out.println("getDefaultTimeout       = " + ftp.getDefaultTimeout());
        System.out.println("getConnectTimeout       = " + ftp.getConnectTimeout());
        System.out.println("getDataTimeout          = " + ftp.getDataTimeout());
        System.out.println("getControlEncoding      = " + ftp.getControlEncoding());
        System.out.println("getAutodetectUTF8       = " + ftp.getAutodetectUTF8());
        System.out.println("getBufferSize           = " + ftp.getBufferSize());
        System.out.println("isRemoteVerificationEnabled = " + ftp.isRemoteVerificationEnabled());
        System.out.println("isUseEPSVwithIPv4       = " + ftp.isUseEPSVwithIPv4());

        TelnetClient telnet = new TelnetClient();
        System.out.println("== TelnetClient 默认值 ==");
        System.out.println("getDefaultPort          = " + telnet.getDefaultPort());
        System.out.println("getConnectTimeout       = " + telnet.getConnectTimeout());
        System.out.println("getDefaultTimeout       = " + telnet.getDefaultTimeout());
        System.out.println("getReaderThread         = " + telnet.getReaderThread());

        SMTPClient smtp = new SMTPClient();
        System.out.println("== SMTPClient 默认值 ==");
        System.out.println("getDefaultPort          = " + smtp.getDefaultPort());

        POP3Client pop3 = new POP3Client();
        System.out.println("== POP3Client 默认值 ==");
        System.out.println("getDefaultPort          = " + pop3.getDefaultPort());

        NTPUDPClient ntp = new NTPUDPClient();
        System.out.println("== NTPUDPClient 默认值 ==");
        System.out.println("NTPUDPClient.DEFAULT_PORT = " + NTPUDPClient.DEFAULT_PORT);
        System.out.println("getDefaultTimeout         = " + ntp.getDefaultTimeout());

        TFTPClient tftp = new TFTPClient();
        System.out.println("== TFTPClient 默认值 ==");
        System.out.println("TFTP.DEFAULT_PORT         = " + org.apache.commons.net.tftp.TFTP.DEFAULT_PORT);

        System.out.println("== FTPClientConfig 解析器标识常量 ==");
        System.out.println("SYST_UNIX=" + FTPClientConfig.SYST_UNIX
                + " SYST_NT=" + FTPClientConfig.SYST_NT
                + " SYST_OS400=" + FTPClientConfig.SYST_OS400
                + " SYST_MVS=" + FTPClientConfig.SYST_MVS
                + " SYST_NETWARE=" + FTPClientConfig.SYST_NETWARE);

        System.out.println("== JVM 系统属性 ==");
        System.out.println("file.encoding  = " + System.getProperty("file.encoding"));
        System.out.println("stdout.encoding= " + System.getProperty("stdout.encoding"));
        System.out.println("java.version   = " + System.getProperty("java.version"));
    }
}

编译运行:

java -Dfile.encoding=UTF-8 -cp target/classes:$HOME/.m2/repository/commons-net/commons-net/3.13.0/commons-net-3.13.0.jar:$HOME/.m2/repository/commons-io/commons-io/2.21.0/commons-io-2.21.0.jar ConfigProbe

实测回显:

== FTPClient 默认值 ==
getDefaultPort          = 21
getDefaultTimeout       = 0
getConnectTimeout       = 60000
getDataTimeout          = PT-0.001S
getControlEncoding      = ISO-8859-1
getAutodetectUTF8       = false
getBufferSize           = 0
isRemoteVerificationEnabled = true
isUseEPSVwithIPv4       = false
== TelnetClient 默认值 ==
getDefaultPort          = 23
getConnectTimeout       = 60000
getDefaultTimeout       = 0
getReaderThread         = true
== SMTPClient 默认值 ==
getDefaultPort          = 25
== POP3Client 默认值 ==
getDefaultPort          = 110
== NTPUDPClient 默认值 ==
NTPUDPClient.DEFAULT_PORT = 123
getDefaultTimeout         = 0
== TFTPClient 默认值 ==
TFTP.DEFAULT_PORT         = 69
== FTPClientConfig 解析器标识常量 ==
SYST_UNIX=UNIX SYST_NT=WINDOWS SYST_OS400=OS/400 SYST_MVS=MVS SYST_NETWARE=NETWARE
== JVM 系统属性 ==
file.encoding  = UTF-8
stdout.encoding= null
java.version   = 17.0.20.1

2.2 用 javap 查静态常量佐证

探针里 getConnectTimeout() 的 60000 不是巧合,SocketClient 基类把它定义成了公开常量:

javap -p -constants -cp $HOME/.m2/repository/commons-net/commons-net/3.13.0/commons-net-3.13.0.jar org.apache.commons.net.SocketClient | grep DEFAULT_CONNECT_TIMEOUT

实测回显:

  public static final int DEFAULT_CONNECT_TIMEOUT = 60000;

验证结果:所有基于 SocketClient 的客户端(FTP/SMTP/POP3/Telnet 等)共享这一默认连接超时;NTPUDPClient、TFTPClient 走 UDP 一族,端口是类常量、超时默认为 0。

三、参数总览

参数作用范围默认值(实测)修改方式
连接超时全部 SocketClient 一系60000mssetConnectTimeout(int)
命令通道读超时SocketClient 一系0(不超时)setDefaultTimeout(int),getSoTimeout() 需连接后读取
数据通道读超时FTPClientPT-0.001S(负值,即未启用)setDataTimeout(Duration)
控制通道编码FTPClientISO-8859-1setControlEncoding(String)
UTF-8 自动探测FTPClientfalsesetAutodetectUTF8(boolean)
传输缓冲区FTPClient0(用系统默认)setBufferSize(int)
被动主机校验FTPClienttruesetRemoteVerificationEnabled(boolean)
IPv4 下用 EPSVFTPClientfalsesetUseEPSVwithIPv4(boolean)
传输模式FTPClient默认主动,按需切换enterLocalPassiveMode() / enterLocalActiveMode()
终端类型TelnetClient无(构造器可指定)new TelnetClient("vt220")
IAC 读取线程TelnetClienttruesetReaderThread(boolean)
SMTP 写通道编码SMTPClientISO-8859-1new SMTPClient("UTF-8")(构造器参数)
服务端口各客户端FTP 21 / SMTP 25 / POP3 110 / Telnet 23 / NTP 123 / TFTP 69connect(host, port) 指定
列表解析器FTPClient按 SYST 自动选择FTPClientConfig + configure()

四、核心参数分类详解

4.1 三个超时:connectTimeout、defaultTimeout、dataTimeout

三者覆盖的时间段完全不同:connectTimeout 管 TCP 建连;defaultTimeout(底层是 socket SO_TIMEOUT)管控制通道上等服务器回复;dataTimeout 管文件传输时数据通道上的读等待。

dataTimeout 的类型在 3.13.0 里是 java.time.Duration(setter/getter 均已升级),实测读取默认值得到 PT-0.001S,即 -1ms——库内部用它表示"未显式设置数据超时",不会对传输产生负数效果,照原样使用也不会抛错。要启用就传正值:

ftp.setDataTimeout(Duration.ofSeconds(60));

defaultTimeout 默认 0 表示永不超时,生产环境务必设置,否则服务端挂起会让读线程永远阻塞。三者生效验证见第五节模板程序的首行输出。

4.2 字符集:controlEncoding、autodetectUTF8 与 JVM 编码

FTP 的控制通道(命令、回复、文件名列表)默认按 ISO-8859-1 编解码,与 JVM 的 file.encoding(本机实测 UTF-8)完全无关。中文文件名场景需要:

ftp.setControlEncoding("UTF-8");
ftp.setAutodetectUTF8(true);

autodetectUTF8=true 后,客户端在收到服务端列表时会尝试按 UTF-8 解码、失败再回退默认编码。注意这组参数只影响 FTP 控制通道;SMTP 的写通道编码是构造器参数(new SMTPClient("UTF-8")),且实测 setCharset() 对它无效——两个坑的完整实验记录见《快速入门与集成示例》。

4.3 传输模式:passive 与 active

💡 技术拓展:FTP 数据连接的建立方向由模式决定:

维度主动模式(PORT)被动模式(PASV/EPSV)
数据连接由谁发起服务端连客户端客户端连服务端
客户端要求可被服务端直连无
服务端要求无放行被动端口段
Commons Net APIenterLocalActiveMode()enterLocalPassiveMode()

本文实测两种模式均与同机 vsftpd 互通,演示程序与生产默认用被动模式。

被动模式下服务端返回的 PASV 回复形如 227 Entering Passive Mode (127,0,0,1,156,144)(本文 FTP 实验中真实抓取),客户端据此发起数据连接。默认开启的 remoteVerificationEnabled 会在建立数据连接后核对对端 IP 是否等于控制连接的服务端 IP,防止被动回跳到第三方地址——若服务端经 NAT 且 PASV 回复的是内网地址,这条安全校验会拦下传输,可 setRemoteVerificationEnabled(false) 关闭(需自担风险),或用 setPassiveLocalIPAddress() 修正。

4.4 缓冲区与 socket 选项

getBufferSize() 实测为 0,表示传输缓冲走系统默认;对大文件吞吐有要求时可用 setBufferSize(int) 指定(如 65536)。setSendBufferSize()/setReceiveBufferSize() 直接作用于 socket 层,需在 connect() 之前设置才生效。setUseEPSVwithIPv4(true) 会让 IPv4 也走 EPSV 扩展被动模式,部分老服务端不支持,默认保持 false。

4.5 列表解析器:FTPClientConfig

listFiles() 依赖对 LIST 输出的文本解析,不同服务端(UNIX/Windows/MVS/NetWare 等)格式差异很大。Commons Net 默认按 SYST 命令结果自动选择解析器,手动指定时构造 FTPClientConfig 并传入解析器标识(2.1 探针实测出的常量值:UNIX、WINDOWS、OS/400、MVS、NETWARE):

FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
ftp.configure(conf);

FTPClientConfig 还支持设置服务端语言代码、时区、日期格式等解析辅助参数;对国内用户最相关的正是 3.13.0 修复的 vsftpd 中文环境解析问题(NET-740),升级版本即可覆盖。

4.6 JVM 与环境参数

Commons Net 自身只消费 JVM 的编码默认值(file.encoding 探针实测 UTF-8)作为日志与控制台输出的参考,协议行为不受它影响。部署侧要管理的环境变量是 JAVA_HOME(CentOS 8 上 Maven 绑定 JDK 的实测案例见《CentOS 8 安装指南》4.3)。没有 commons-net.* 之类的系统属性。

五、生产环境参数模板

把上述参数按生产口径组合起来,与同机 vsftpd 实测验证:

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.time.Duration;

public class FtpProd {
    public static void main(String[] args) throws Exception {
        FTPClient ftp = new FTPClient();
        ftp.setConnectTimeout(10000);
        ftp.setDefaultTimeout(30000);
        ftp.setDataTimeout(Duration.ofSeconds(60));
        ftp.setControlEncoding("UTF-8");
        ftp.setAutodetectUTF8(true);
        ftp.setRemoteVerificationEnabled(true);
        System.out.println("生效参数: connectTimeout=" + ftp.getConnectTimeout()
                + " defaultTimeout=" + ftp.getDefaultTimeout()
                + " dataTimeout=" + ftp.getDataTimeout()
                + " controlEncoding=" + ftp.getControlEncoding()
                + " autodetectUTF8=" + ftp.getAutodetectUTF8());

        ftp.connect("127.0.0.1", 21);
        if (!ftp.login("ftpuser", "q1w2e3r4")) {
            ftp.disconnect();
            throw new IllegalStateException("登录失败: " + ftp.getReplyString());
        }
        ftp.enterLocalPassiveMode();
        ftp.setFileType(FTP.BINARY_FILE_TYPE);
        File local = new File("/root/commons-net-demo/hello-ftp.txt");
        try (InputStream in = new FileInputStream(local)) {
            System.out.println("生产参数下 storeFile = " + ftp.storeFile("prod-check.txt", in)
                    + " 回复码=" + ftp.getReplyCode());
        }
        ftp.logout();
        ftp.disconnect();
        System.out.println("生产参数链路验证通过");
    }
}

实测回显:

生效参数: connectTimeout=10000 defaultTimeout=30000 dataTimeout=PT60S controlEncoding=UTF-8 autodetectUTF8=true
生产参数下 storeFile = true 回复码=226
生产参数链路验证通过

验证结果:三段超时全部按设置生效(dataTimeout 读回 PT60S),UTF-8 控制通道下上传正常,服务端落盘文件 prod-check.txt 已确认存在。SMTP/Telnet 客户端照此口径各设 setConnectTimeout 与 setDefaultTimeout 即可。

六、常见问题(FAQ)

  1. 连接前调 getSoTimeout() 直接 NPE。socket 还没建立,读 socket 选项必然空指针,实测堆栈:

    Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.net.Socket.getSoTimeout()" because "this._socket_" is null
            at org.apache.commons.net.SocketClient.getSoTimeout(SocketClient.java:540)
    

    配置期用 getDefaultTimeout() 查看设置值,getSoTimeout() 只在 connect() 之后调用。

  2. getDataTimeout() 读出 PT-0.001S 是坏了吗?没坏,-1ms 是库内部"未显式设置"的哨兵值,实测不影响传输;按第五节传正数 Duration 即覆盖。

  3. 连接超时到底是不是 0(不超时)?3.13.0 不是。SocketClient.DEFAULT_CONNECT_TIMEOUT = 60000(2.2 javap 实测),新建实例即有 60 秒建连超时;老版本代码里 setConnectTimeout(0) 仍可显式关掉。

  4. new SMTPClient() 发中文变问号,setCharset 又不生效?SMTP 写通道编码在构造时固定为 ISO-8859-1,用 new SMTPClient("UTF-8"),sink 侧字节级对照实验见《快速入门与集成示例》4.3。

  5. 中文文件名列表解析失败?先升级到 3.13.0(NET-740 修复 vsftpd 中文环境解析),再按 4.2 设置 setControlEncoding("UTF-8") + setAutodetectUTF8(true)。

  6. bufferSize 设多大合适?默认 0(系统默认)在千兆内网已够用;万兆或高延迟链路再试 65536 级别,实测收益与文件大小和 RTT 强相关,建议在自己的环境压测后再定。

七、参考链接