You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android VPNService调用establish后DatagramChannel无法发送数据包

Android VPNService调用establish后UDP连接失效问题解决

问题核心原因

调用VPNService.establish()后,系统会修改全局路由表,将所有网络流量导向TUN虚拟设备。此时之前创建的UDP socket基于旧路由表绑定物理网卡,无法再通过新路由发送数据包;同时若VPNService未处理TUN设备的数据包读写,流量会直接被丢弃。

解决步骤

1. 在establish()后重新创建UDP连接

废弃调用establish()前创建的所有UDP socket,在TUN设备成功建立后,重新初始化UDP连接:

// 先调用establish()创建TUN设备
ParcelFileDescriptor pfd = builder.establish();
if (pfd != null) {
    // 重新创建UDP socket并发送测试数据
    DatagramSocket udpSocket = new DatagramSocket();
    InetAddress serverAddr = InetAddress.getByName("你的服务器IP");
    int serverPort = 你的服务器端口;
    byte[] data = "Hello DPN Server Side".getBytes();
    DatagramPacket packet = new DatagramPacket(data, data.length, serverAddr, serverPort);
    udpSocket.send(packet);
}

2. 实现TUN设备的数据包转发逻辑

TUN设备仅负责接收和发送IP层数据包,必须在VPNService中启动线程持续处理读写:

private void startTunIoThread() {
    new Thread(() -> {
        try (FileInputStream in = getInputStream();
             FileOutputStream out = getOutputStream()) {
            byte[] buffer = new byte[1500]; // 匹配网络MTU值
            int readLen;
            while ((readLen = in.read(buffer)) != -1) {
                // 将读取到的IP数据包转发到远程服务器
                sendIpPacketToServer(buffer, readLen);
                // 接收服务器返回的IP数据包,写入TUN输出流
                byte[] responsePacket = receiveIpPacketFromServer();
                if (responsePacket != null) {
                    out.write(responsePacket);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }, "TunIoThread").start();
}

// 示例:用UDP转发IP数据包到服务器
private void sendIpPacketToServer(byte[] packet, int length) throws IOException {
    DatagramSocket socket = new DatagramSocket();
    InetAddress serverAddr = InetAddress.getByName("你的服务器IP");
    DatagramPacket sendPacket = new DatagramPacket(packet, length, serverAddr, 服务器端口);
    socket.send(sendPacket);
    socket.close();
}

3. 检查并修正VPN路由配置

确保Builder配置了正确的虚拟IP和全局路由,让所有流量都进入TUN设备:

Builder vpnBuilder = new Builder();
// 设置TUN设备的虚拟IP与子网掩码
vpnBuilder.addAddress("10.0.0.1", 24);
// 添加全局路由,覆盖所有IPv4和IPv6流量
vpnBuilder.addRoute("0.0.0.0", 0);
vpnBuilder.addRoute("::", 0);
// 设置VPN会话名称
vpnBuilder.setSession("Pet VPN");
// 建立TUN设备
ParcelFileDescriptor pfd = vpnBuilder.establish();

4. 处理PortUnreachableException

该异常是旧UDP socket在路由变更后无法正常寻址导致的,只要在establish()后重新创建socket即可解决,无需针对旧socket做额外处理。

内容的提问来源于stack exchange,提问作者Yalkap Gurbanow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 20:22:44