Apache Commons Net 3.13.0 配置参数详解
用探针程序实测 Apache Commons Net 3.13.0 各客户端的默认参数,逐项给出修改方式、生效验证与生产模板,覆盖超时、字符集、传输模式与列表解析器
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 一系 | 60000ms | setConnectTimeout(int) |
| 命令通道读超时 | SocketClient 一系 | 0(不超时) | setDefaultTimeout(int),getSoTimeout() 需连接后读取 |
| 数据通道读超时 | FTPClient | PT-0.001S(负值,即未启用) | setDataTimeout(Duration) |
| 控制通道编码 | FTPClient | ISO-8859-1 | setControlEncoding(String) |
| UTF-8 自动探测 | FTPClient | false | setAutodetectUTF8(boolean) |
| 传输缓冲区 | FTPClient | 0(用系统默认) | setBufferSize(int) |
| 被动主机校验 | FTPClient | true | setRemoteVerificationEnabled(boolean) |
| IPv4 下用 EPSV | FTPClient | false | setUseEPSVwithIPv4(boolean) |
| 传输模式 | FTPClient | 默认主动,按需切换 | enterLocalPassiveMode() / enterLocalActiveMode() |
| 终端类型 | TelnetClient | 无(构造器可指定) | new TelnetClient("vt220") |
| IAC 读取线程 | TelnetClient | true | setReaderThread(boolean) |
| SMTP 写通道编码 | SMTPClient | ISO-8859-1 | new SMTPClient("UTF-8")(构造器参数) |
| 服务端口 | 各客户端 | FTP 21 / SMTP 25 / POP3 110 / Telnet 23 / NTP 123 / TFTP 69 | connect(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 API enterLocalActiveMode()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)
-
连接前调
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()之后调用。 -
getDataTimeout()读出PT-0.001S是坏了吗?没坏,-1ms 是库内部"未显式设置"的哨兵值,实测不影响传输;按第五节传正数Duration即覆盖。 -
连接超时到底是不是 0(不超时)?3.13.0 不是。
SocketClient.DEFAULT_CONNECT_TIMEOUT = 60000(2.2 javap 实测),新建实例即有 60 秒建连超时;老版本代码里setConnectTimeout(0)仍可显式关掉。 -
new SMTPClient()发中文变问号,setCharset又不生效?SMTP 写通道编码在构造时固定为 ISO-8859-1,用new SMTPClient("UTF-8"),sink 侧字节级对照实验见《快速入门与集成示例》4.3。 -
中文文件名列表解析失败?先升级到 3.13.0(NET-740 修复 vsftpd 中文环境解析),再按 4.2 设置
setControlEncoding("UTF-8")+setAutodetectUTF8(true)。 -
bufferSize 设多大合适?默认 0(系统默认)在千兆内网已够用;万兆或高延迟链路再试 65536 级别,实测收益与文件大小和 RTT 强相关,建议在自己的环境压测后再定。
七、参考链接
- JavaDoc SocketClient(超时与选项):https://commons.apache.org/proper/commons-net/javadocs/api-3.13.0/org/apache/commons/net/SocketClient.html
- JavaDoc FTPClient(FTP 参数全集):https://commons.apache.org/proper/commons-net/javadocs/api-3.13.0/org/apache/commons/net/ftp/FTPClient.html
- JavaDoc FTPClientConfig(解析器参数):https://commons.apache.org/proper/commons-net/javadocs/api-3.13.0/org/apache/commons/net/ftp/FTPClientConfig.html
- RELEASE-NOTES(3.13.0):https://downloads.apache.org/commons/net/RELEASE-NOTES.txt
- 本系列:《Apache Commons Net 3.13.0 快速入门与集成示例》《Apache Commons Net 3.13.0 常用 API 与网络客户端用法详解》