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
相关产品推荐
相关产品推荐

