Java 代码通过系统命令行调用操作系统的原生工具(在 Windows 上是 rasdial,在 Linux/macOS 上是 pppd 或 nmcli 等)来完成拨号连接。

下面我将分步详细解释整个过程,包括原理、代码示例和关键注意事项。
核心原理
-
USB 设备作为网络串口 (RNDIS/NCM):当你的手机(或其他支持此功能的 USB 设备)通过 USB 线连接到电脑并选择“互联网共享”或“USB 网络共享”模式时,它会被电脑识别为一个虚拟网卡,而不是一个串口,操作系统会自动为其分配一个 IP 地址,并创建一个新的网络连接(在 Windows 上叫“USB RNDIS 网络适配器”)。
-
操作系统提供拨号工具:这个虚拟网卡本身已经“连接”到了手机,但要让电脑通过它真正访问互联网,通常还需要一个“拨号”或“连接”的步骤,这个过程被称为 PPP (Point-to-Point Protocol) 连接,操作系统为此提供了命令行工具。
- Windows:
rasdial.exe - Linux:
pppd或nmcli(NetworkManager CLI) - macOS:
pppd或networksetup
- Windows:
-
Java 作为执行者:Java 的
Runtime.getRuntime().exec()或ProcessBuilder类可以让我们在 Java 程序中执行这些系统命令,我们的 Java 代码就是负责调用这些工具,并传入正确的参数(如 APN、用户名、密码等)来启动连接。
(图片来源网络,侵删)
实现步骤
第 1 步:准备工作
在写代码之前,你必须确保:
- 硬件连接:将你的手机(或其他 USB 设备)通过 USB 线连接到电脑。
- 手机端设置:在手机上找到“个人热点”或“网络共享”选项,并选择“通过 USB 共享网络”,电脑的系统应该会提示发现新硬件并安装驱动,最终出现一个可用的网络适配器。
- 获取连接信息:这是最关键的一步,你需要从手机运营商或网络设置中获取拨号所需的信息,通常是:
- APN (Access Point Name):
cmnet(中国移动),3gnet(中国联通),ctnet(中国电信)。 - 用户名: 大部分情况下为空或
card。 - 密码: 大部分情况下为空或
card。 - 连接名称: 你可以为这个连接起一个名字,
MyUSBInternet。
- APN (Access Point Name):
第 2 步:Java 代码实现
我们将提供 Windows 和 Linux/macOS 两个平台的代码示例。
Windows 示例 (使用 rasdial)
rasdial 命令的基本格式是:
rasdial "连接名称" "用户名" "密码"
如果用户名和密码为空,则省略它们。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class UsbInternetConnectorWindows {
public static void main(String[] args) {
// --- 配置参数 ---
String connectionName = "MyUSBInternet"; // 你在Windows网络连接中看到的名称,或自定义名称
String username = ""; // 大部分为空
String password = ""; // 大部分为空
String apn = "cmnet"; // 你的APN,某些情况下可能需要作为用户名或密码传入
// 注意:有些手机/运营商可能需要将APN作为用户名或密码。
// username = apn; password = "";
// 这需要根据实际情况尝试。
// 构建命令
// 如果用户名和密码为空,命令为 "rasdial \"连接名称\""
// 否则为 "rasdial \"连接名称\" \"用户名\" \"密码\""
String[] command;
if (username.isEmpty() && password.isEmpty()) {
command = new String[]{"cmd.exe", "/c", "rasdial", "\"" + connectionName + "\""};
} else {
command = new String[]{"cmd.exe", "/c", "rasdial", "\"" + connectionName + "\"", "\"" + username + "\"", "\"" + password + "\""};
}
try {
System.out.println("正在执行命令: " + String.join(" ", command));
// 使用 ProcessBuilder 更灵活
ProcessBuilder pb = new ProcessBuilder(command);
pb.redirectErrorStream(true); // 合并错误流和输出流
Process process = pb.start();
// 读取命令的输出
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK")); // Windows命令行常用GBK编码
String line;
System.out.println("命令输出:");
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// 等待命令执行完毕
int exitCode = process.waitFor();
if (exitCode == 0) {
System.out.println("连接成功!");
// --- 在这里编写你的网络应用代码 ---
// 发送HTTP请求
// HttpClientExample.sendGetRequest("http://www.baidu.com");
// -------------------------------------
} else {
System.out.println("连接失败!退出码: " + exitCode);
}
reader.close();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
Linux/macOS 示例 (使用 nmcli 或 pppd)
在现代 Linux 发行版(如 Ubuntu, Fedora)和 macOS 上,推荐使用 nmcli (NetworkManager 的命令行工具),因为它更简单、更可靠。
使用 nmcli (推荐)
nmcli 命令会先添加一个连接,再激活它。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class UsbInternetConnectorLinux {
public static void main(String[] args) {
// --- 配置参数 ---
String connectionName = "MyUSBInternet";
String ifaceName = "enp0s20f0u2"; // 这是关键!必须是你USB网卡的实际设备名,如 'eth1', 'enx...', 'usb0' 等
String apn = "cmnet";
String username = "";
String password = "";
try {
// 1. 添加一个新的PPP连接
// nmcli connection add type gsm ifname <设备名> con-name <连接名> apn <APN> username <用户名> password <密码>
String[] addCommand = {
"nmcli", "connection", "add", "type", "gsm",
"ifname", ifaceName,
"con-name", connectionName,
"apn", apn,
"username", username,
"password", password
};
System.out.println("正在添加连接...");
executeCommand(addCommand);
// 2. 激活该连接
// nmcli connection up <连接名>
String[] upCommand = {
"nmcli", "connection", "up", connectionName
};
System.out.println("正在激活连接...");
executeCommand(upCommand);
System.out.println("连接成功!");
// --- 在这里编写你的网络应用代码 ---
// 3. (可选) 断开连接
// String[] downCommand = {"nmcli", "connection", "down", connectionName};
// executeCommand(downCommand);
// System.out.println("已断开连接。");
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
private static void executeCommand(String[] command) throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder(command);
pb.redirectErrorStream(true);
Process process = pb.start();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IOException("命令执行失败,退出码: " + exitCode);
}
}
}
如何找到网卡设备名 (ifaceName)?
在 Linux/macOS 终端中运行 ip a 或 ifconfig 命令,你会看到类似下面的输出,找到那个在你插入USB后新出现的接口,它的名字就是你要的。
$ ip a
...
2: enp0s20f0u2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 12:34:56:78:9a:bc brd ff:ff:ff:ff:ff:ff
inet 169.254.123.45/16 scope global enp0s20f0u2
valid_lft forever preferred_lft forever
...
这里的 enp0s20f0u2 就是设备名。
关键注意事项和难点
-
权限问题:执行这些系统命令通常需要管理员/超级用户权限。
- Windows: 你需要以管理员身份运行你的 Java IDE 或打包后的
.jar文件。 - Linux/macOS: 你的 Java 进程需要有
sudo权限,最简单的方式是使用sudo来运行你的 Java 程序,或者在sudoers文件中配置允许特定用户执行nmcli或pppd命令而无需密码。
- Windows: 你需要以管理员身份运行你的 Java IDE 或打包后的
-
平台依赖性:上面的代码是平台特定的,你需要检测当前操作系统,然后调用相应的命令,一个简单的判断方法:
String os = System.getProperty("os.name").toLowerCase(); if (os.contains("win")) { // 执行Windows命令 } else if (os.contains("nix") || os.contains("nux") || os.contains("mac")) { // 执行Linux/macOS命令 } -
APN、用户名、密码的多样性:不同运营商、不同手机型号,甚至不同地区的 APN 设置都可能不同,APN、用户名、密码的组合需要用户自行配置,或者你的程序需要提供一个界面让用户输入。
-
驱动问题:如果电脑没有正确识别 USB 网卡,或者缺少相应的 RNDIS/NCM 驱动,那么任何 Java 代码都无法解决问题,你需要先确保在操作系统能手动创建和连接网络。
-
异步处理和状态监控:上面的代码是同步的,会阻塞直到连接建立或失败,在实际应用中,你可能需要异步执行,并监控连接状态。
nmcli提供了nmcli connection show --active来查看当前活动的连接。
在 Java 中“打开”USB 互联网,本质上是利用 Java 作为桥梁,去调用操作系统底层的网络连接工具。
流程回顾:
- 物理连接:手机开启 USB 共享。
- 系统识别:操作系统识别出 USB 虚拟网卡。
- Java 调用:Java 程序以管理员权限执行系统拨号命令(
rasdial/nmcli)。 - 建立连接:系统工具完成 PPP 握手,建立网络连接。
- 应用使用:Java 的网络库(如
HttpURLConnection,HttpClient)就可以通过这个新连接访问互联网了。
虽然过程比直接调用一个 API 要复杂,但这是目前 Java 在跨平台场景下实现此功能的唯一可靠途径。
