在 Java 开发中,网络编程(如 FTP 文件传输、HTTP 请求、Telnet 远程控制)是高频需求,但 JDK 原生 API 存在配置繁琐、功能单一、异常处理复杂等问题。Apache Commons Net 作为一款成熟的开源网络工具库,封装了 FTP、FTPS、HTTP、Telnet、SMTP 等多种网络协议的实现,大幅简化网络操作代码,提升开发效率。本文从环境搭建、核心功能实操、常见场景案例三个维度,详解 Commons Net 的使用方法,附精简代码示例,帮助开发者快速落地网络相关需求。

一、核心介绍与环境准备

1. Commons Net 核心特性

  • 支持多种网络协议:FTP/FTPS、HTTP/HTTPS、Telnet、SMTP、POP3、NNTP 等;
  • API 简洁易用:封装原生 Socket 操作,屏蔽底层复杂细节;
  • 稳定可靠:经过长期迭代优化,广泛应用于生产环境;
  • 轻量级:依赖少,引入后不增加项目体积负担。

2. 环境配置(Maven/Gradle)

(1)Maven 依赖(推荐)

在 pom.xml 中添加 Commons Net 依赖(最新版本可从 Maven 中央仓库 查询):


<!-- Apache Commons Net 核心依赖 -->

<dependency>

<groupId>commons-net</groupId>

<artifactId>commons-net</artifactId>

<version>3.10.0</version> <!-- 稳定版本,兼容 Java 8+ -->

</dependency>

(2)Gradle 依赖

implementation 'commons-net:commons-net:3.10.0'

(3)手动导入(无构建工具)

Apache 官网 下载 commons-net-3.10.0.jar,添加到项目 lib 目录并配置依赖。

3. 核心类结构

Commons Net 按协议分类封装核心类,常用类如下:

  • FTP 相关:FTPClient(FTP 客户端)、FTPSClient(FTPS 安全客户端);
  • HTTP 相关:HttpClient(HTTP 客户端,注意:非 Apache HttpClient 库,功能较基础);
  • Telnet 相关:TelnetClient(Telnet 客户端);
  • 工具类:SocketClient(所有客户端基类)、IOUtils(IO 流工具,简化文件传输)。

二、核心功能实操:高频场景案例

场景 1:FTP 服务器文件上传与下载(最常用)

FTPClient 是 Commons Net 最核心的类之一,支持 FTP 服务器连接、文件上传、下载、目录操作等功能。

(1)FTP 文件上传示例

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

import java.io.FileInputStream;

import java.io.IOException;

public class FtpUploadDemo {

// FTP 服务器配置

private static final String FTP_HOST = "ftp.example.com"; // 服务器地址

private static final int FTP_PORT = 21; // 端口(默认 21)

private static final String FTP_USER = "username"; // 用户名

private static final String FTP_PASS = "password"; // 密码

public static void main(String[] args) {

FTPClient ftpClient = new FTPClient();

FileInputStream fis = null;

try {

// 1. 连接 FTP 服务器

ftpClient.connect(FTP_HOST, FTP_PORT);

// 2. 登录

boolean loginSuccess = ftpClient.login(FTP_USER, FTP_PASS);

if (!loginSuccess) {

System.out.println("FTP 登录失败");

return;

}

System.out.println("FTP 登录成功");

// 3. 配置上传参数(二进制文件传输,避免乱码)

ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);

ftpClient.enterLocalPassiveMode(); // 被动模式(推荐,避免防火墙拦截)

// 4. 本地文件路径与 FTP 服务器目标路径

String localFilePath = "D:/test.pdf"; // 本地文件

String remoteFilePath = "/upload/test.pdf"; // 服务器路径(需提前创建目录)

// 5. 上传文件

fis = new FileInputStream(localFilePath);

boolean uploadSuccess = ftpClient.storeFile(remoteFilePath, fis);

if (uploadSuccess) {

System.out.println("文件上传成功");

} else {

System.out.println("文件上传失败:" + ftpClient.getReplyString());

}

} catch (IOException e) {

e.printStackTrace();

} finally {

// 6. 资源释放

try {

if (fis != null) fis.close();

if (ftpClient.isConnected()) {

ftpClient.logout(); // 退出登录

ftpClient.disconnect(); // 断开连接

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

(2)FTP 文件下载示例

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

import java.io.FileOutputStream;

import java.io.IOException;

public class FtpDownloadDemo {

private static final String FTP_HOST = "ftp.example.com";

private static final int FTP_PORT = 21;

private static final String FTP_USER = "username";

private static final String FTP_PASS = "password";

public static void main(String[] args) {

FTPClient ftpClient = new FTPClient();

FileOutputStream fos = null;

try {

// 1. 连接并登录

ftpClient.connect(FTP_HOST, FTP_PORT);

if (!ftpClient.login(FTP_USER, FTP_PASS)) {

System.out.println("FTP 登录失败");

return;

}

// 2. 配置下载参数

ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);

ftpClient.enterLocalPassiveMode();

// 3. FTP 服务器文件路径与本地存储路径

String remoteFilePath = "/download/test.pdf"; // 服务器文件

String localFilePath = "D:/download/test.pdf"; // 本地存储路径

// 4. 下载文件

fos = new FileOutputStream(localFilePath);

boolean downloadSuccess = ftpClient.retrieveFile(remoteFilePath, fos);

if (downloadSuccess) {

System.out.println("文件下载成功");

} else {

System.out.println("文件下载失败:" + ftpClient.getReplyString());

}

} catch (IOException e) {

e.printStackTrace();

} finally {

// 5. 释放资源

try {

if (fos != null) fos.close();

if (ftpClient.isConnected()) {

ftpClient.logout();

ftpClient.disconnect();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

关键说明
  • 文件类型:必须设置 setFileType,二进制文件(如 PDF、图片)用 BINARY_FILE_TYPE,文本文件用 ASCII_FILE_TYPE,避免文件损坏;
  • 传输模式:enterLocalPassiveMode() 启用被动模式,解决主动模式下客户端防火墙拦截问题(生产环境优先使用);
  • 错误排查:getReplyString() 可获取 FTP 服务器返回的错误信息,便于调试。

场景 2:Telnet 远程登录与命令执行

TelnetClient 支持 Telnet 协议,可远程登录服务器执行命令(如 Linux 服务器操作)。


import org.apache.commons.net.telnet.TelnetClient;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.PrintStream;

import java.util.Scanner;

public class TelnetDemo {

private static final String TELNET_HOST = "192.168.1.100"; // 远程服务器 IP

private static final int TELNET_PORT = 23; // 默认端口 23

private static final String USER = "root"; // 登录用户名

private static final String PASS = "123456"; // 登录密码

public static void main(String[] args) {

TelnetClient telnetClient = new TelnetClient();

InputStream in = null;

OutputStream out = null;

PrintStream ps = null;

Scanner scanner = new Scanner(System.in);

try {

// 1. 连接 Telnet 服务器

telnetClient.connect(TELNET_HOST, TELNET_PORT);

System.out.println("Telnet 连接成功");

// 2. 获取输入输出流(用于交互)

in = telnetClient.getInputStream();

out = telnetClient.getOutputStream();

ps = new PrintStream(out);

// 3. 登录流程(模拟手动输入用户名密码)

readResponse(in); // 读取服务器欢迎信息

ps.println(USER); // 发送用户名

readResponse(in); // 读取密码提示

ps.println(PASS); // 发送密码

readResponse(in); // 读取登录成功后的提示

// 4. 执行命令(如查看目录)

String command = "ls -l"; // Linux 命令

ps.println(command);

readResponse(in); // 读取命令执行结果

// 5. 手动输入命令(可选)

while (true) {

System.out.print("输入命令(exit 退出):");

String cmd = scanner.nextLine();

ps.println(cmd);

if ("exit".equals(cmd)) break;

readResponse(in);

}

} catch (Exception e) {

e.printStackTrace();

} finally {

// 6. 资源释放

try {

if (ps != null) ps.close();

if (out != null) out.close();

if (in != null) in.close();

if (telnetClient.isConnected()) {

telnetClient.disconnect();

}

scanner.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

// 读取服务器响应(解决中文乱码问题)

private static void readResponse(InputStream in) throws Exception {

byte[] buffer = new byte[1024];

int len;

while ((len = in.read(buffer)) != -1) {

String response = new String(buffer, 0, len, "UTF-8");

System.out.print(response);

// 简单判断是否读取完毕(根据命令提示符判断,需根据服务器调整)

if (response.endsWith("$ ") || response.endsWith("# ")) {

break;

}

}

}

}

场景 3:HTTP 简单请求(基础功能)

Commons Net 的 HttpClient 功能较基础,适合简单的 HTTP GET/POST 请求(复杂场景推荐使用 Apache HttpClient 或 OkHttp 库)。


import org.apache.commons.net.http.HttpClient;

import org.apache.commons.net.http.HttpMethod;

import org.apache.commons.net.http.GetMethod;

import java.io.IOException;

public class HttpGetDemo {

public static void main(String[] args) {

HttpClient httpClient = new HttpClient();

HttpMethod getMethod = null;

try {

// 1. 配置请求参数

String url = "https://api.example.com/data"; // 请求地址

getMethod = new GetMethod(url);

getMethod.setRequestHeader("User-Agent", "Mozilla/5.0"); // 设置请求头

// 2. 执行 GET 请求

int statusCode = httpClient.executeMethod(getMethod);

System.out.println("响应状态码:" + statusCode);

// 3. 读取响应内容

if (statusCode == 200) { // 成功

String response = getMethod.getResponseBodyAsString();

System.out.println("响应内容:" + response);

}

} catch (IOException e) {

e.printStackTrace();

} finally {

// 4. 释放资源

if (getMethod != null) {

getMethod.releaseConnection();

}

}

}

}

三、进阶优化与避坑指南

1. 连接超时与读取超时配置

避免网络异常导致程序阻塞,需设置超时时间:


// FTP 客户端超时配置(单位:毫秒)

ftpClient.setConnectTimeout(5000); // 连接超时(5秒)

ftpClient.setDataTimeout(10000); // 数据传输超时(10秒)

// Telnet 客户端超时配置

telnetClient.setConnectTimeout(5000);

2. 中文乱码问题解决方案

  • FTP 文件名中文乱码:设置字符编码为 UTF-8:

ftpClient.setControlEncoding("UTF-8"); // 控制通道编码

ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); // 二进制传输避免内容乱码

  • Telnet 响应中文乱码:读取时指定 UTF-8 编码(如场景 2 中 readResponse 方法)。

3. 常见坑点与解决方案

问题现象

原因分析

解决方案

FTP 上传失败,返回 550 错误

服务器目标路径不存在或权限不足

提前创建目录(ftpClient.makeDirectory("/upload")),检查用户权限

Telnet 连接超时

服务器未开启 Telnet 服务或端口被防火墙拦截

确认服务器 Telnet 服务已启动,开放 23 端口

HTTP 请求返回 403 错误

服务器拒绝请求(缺少请求头)

设置 User-Agent 等请求头,模拟浏览器请求

文件传输中断

网络不稳定或超时时间过短

增大超时时间,实现断点续传(FTPClient 支持 restartOffset)

4. 断点续传实现(FTP 进阶功能)

大文件传输时,可通过 setRestartOffset 实现断点续传:


// 断点续传下载(从第 1024 字节开始下载)

long restartOffset = 1024;

ftpClient.setRestartOffset(restartOffset);

// 下载时在输出流中定位到对应位置

fos = new FileOutputStream(localFilePath, true); // 追加模式

fos.skip(restartOffset);

ftpClient.retrieveFile(remoteFilePath, fos);

四、适用场景与扩展建议

1. 适合使用 Commons Net 的场景

  • 简单 FTP/FTPS 文件传输(如项目配置文件上传、日志下载);
  • Telnet 远程控制(如嵌入式设备、路由器配置);
  • 轻量级 HTTP 请求(无需复杂拦截器、重试机制);
  • 传统网络协议开发(如 SMTP 邮件发送、POP3 邮件接收)。

2. 不适合的场景与替代方案

  • 复杂 HTTP 需求(如 HTTPS、请求重试、拦截器):推荐 Apache HttpClient 或 OkHttp;
  • 高性能文件传输:推荐使用 SFTP(SSH File Transfer Protocol),可结合 JSch 库实现;
  • 分布式环境下的网络通信:推荐使用 Netty 框架(异步非阻塞)。

3. 扩展功能建议

  • 封装工具类:将 FTP/Telnet 操作封装为单例工具类,简化重复代码;
  • 日志记录:集成 SLF4J 等日志框架,记录网络操作日志(便于问题排查);
  • 异常重试:使用 Guava Retryer 实现失败重试机制(如 FTP 连接重试)。

总结

Apache Commons Net 是 Java 网络编程的 “瑞士军刀”,凭借简洁的 API 与丰富的协议支持,大幅降低 FTP、Telnet 等传统网络操作的开发成本。核心使用场景集中在文件传输、远程控制等基础网络需求,通过本文的示例代码,可快速实现上传下载、命令执行等功能。

使用时需注意配置超时时间、解决中文乱码、处理权限问题等关键要点,避免常见坑点。对于复杂场景(如高性能 HTTP、SFTP),可选择更专业的框架,但 Commons Net 仍是基础网络操作的高效选择。建议结合实际需求封装工具类,提升代码复用性,让网络编程更简洁、高效。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐