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

java 打开usb互联网
(图片来源网络,侵删)

下面我将分步详细解释整个过程,包括原理、代码示例和关键注意事项。


核心原理

  1. USB 设备作为网络串口 (RNDIS/NCM):当你的手机(或其他支持此功能的 USB 设备)通过 USB 线连接到电脑并选择“互联网共享”或“USB 网络共享”模式时,它会被电脑识别为一个虚拟网卡,而不是一个串口,操作系统会自动为其分配一个 IP 地址,并创建一个新的网络连接(在 Windows 上叫“USB RNDIS 网络适配器”)。

  2. 操作系统提供拨号工具:这个虚拟网卡本身已经“连接”到了手机,但要让电脑通过它真正访问互联网,通常还需要一个“拨号”或“连接”的步骤,这个过程被称为 PPP (Point-to-Point Protocol) 连接,操作系统为此提供了命令行工具。

    • Windows: rasdial.exe
    • Linux: pppdnmcli (NetworkManager CLI)
    • macOS: pppdnetworksetup
  3. Java 作为执行者:Java 的 Runtime.getRuntime().exec()ProcessBuilder 类可以让我们在 Java 程序中执行这些系统命令,我们的 Java 代码就是负责调用这些工具,并传入正确的参数(如 APN、用户名、密码等)来启动连接。

    java 打开usb互联网
    (图片来源网络,侵删)

实现步骤

第 1 步:准备工作

在写代码之前,你必须确保:

  1. 硬件连接:将你的手机(或其他 USB 设备)通过 USB 线连接到电脑。
  2. 手机端设置:在手机上找到“个人热点”或“网络共享”选项,并选择“通过 USB 共享网络”,电脑的系统应该会提示发现新硬件并安装驱动,最终出现一个可用的网络适配器。
  3. 获取连接信息:这是最关键的一步,你需要从手机运营商或网络设置中获取拨号所需的信息,通常是:
    • APN (Access Point Name): cmnet (中国移动), 3gnet (中国联通), ctnet (中国电信)。
    • 用户名: 大部分情况下为空或 card
    • 密码: 大部分情况下为空或 card
    • 连接名称: 你可以为这个连接起一个名字,MyUSBInternet

第 2 步:Java 代码实现

我们将提供 Windows 和 Linux/macOS 两个平台的代码示例。

Windows 示例 (使用 rasdial)

rasdial 命令的基本格式是: rasdial "连接名称" "用户名" "密码"

如果用户名和密码为空,则省略它们。

java 打开usb互联网
(图片来源网络,侵删)
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 示例 (使用 nmclipppd)

在现代 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 aifconfig 命令,你会看到类似下面的输出,找到那个在你插入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 就是设备名。


关键注意事项和难点

  1. 权限问题:执行这些系统命令通常需要管理员/超级用户权限。

    • Windows: 你需要以管理员身份运行你的 Java IDE 或打包后的 .jar 文件。
    • Linux/macOS: 你的 Java 进程需要有 sudo 权限,最简单的方式是使用 sudo 来运行你的 Java 程序,或者在 sudoers 文件中配置允许特定用户执行 nmclipppd 命令而无需密码。
  2. 平台依赖性:上面的代码是平台特定的,你需要检测当前操作系统,然后调用相应的命令,一个简单的判断方法:

    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命令
    }
  3. APN、用户名、密码的多样性:不同运营商、不同手机型号,甚至不同地区的 APN 设置都可能不同,APN、用户名、密码的组合需要用户自行配置,或者你的程序需要提供一个界面让用户输入。

  4. 驱动问题:如果电脑没有正确识别 USB 网卡,或者缺少相应的 RNDIS/NCM 驱动,那么任何 Java 代码都无法解决问题,你需要先确保在操作系统能手动创建和连接网络。

  5. 异步处理和状态监控:上面的代码是同步的,会阻塞直到连接建立或失败,在实际应用中,你可能需要异步执行,并监控连接状态。nmcli 提供了 nmcli connection show --active 来查看当前活动的连接。

在 Java 中“打开”USB 互联网,本质上是利用 Java 作为桥梁,去调用操作系统底层的网络连接工具

流程回顾

  1. 物理连接:手机开启 USB 共享。
  2. 系统识别:操作系统识别出 USB 虚拟网卡。
  3. Java 调用:Java 程序以管理员权限执行系统拨号命令(rasdial / nmcli)。
  4. 建立连接:系统工具完成 PPP 握手,建立网络连接。
  5. 应用使用:Java 的网络库(如 HttpURLConnection, HttpClient)就可以通过这个新连接访问互联网了。

虽然过程比直接调用一个 API 要复杂,但这是目前 Java 在跨平台场景下实现此功能的唯一可靠途径。