Android VPN接口引发ERR_NETWORK_CHANGED错误的原因排查求助
问题:Android VPN API捕获数据包后无法加载网页,出现ERR_NETWORK_CHANGED错误
我正在Android Studio中开发一款需要监控用户网页浏览行为的Kotlin应用,尝试使用Android VPN API实现该功能,代码编译运行正常,日志显示已成功捕获数据包:
日志片段:
2024-08-07 13:52:02.492 18270-18311 WebFilterVpnService com.example.pbapp D Packet captured from 10.0.0.2 to 142.250.68.227 2024-08-07 13:52:02.525 18270-18311 WebFilterVpnService com.example.pbapp D Packet captured from 10.0.0.2 to 216.239.32.116 2024-08-07 13:52:02.589 18270-18311 WebFilterVpnService com.example.pbapp D Packet captured from 10.0.0.2 to 216.239.32.116 2024-08-07 13:52:02.589 18270-18311 WebFilterVpnService com.example.pbapp D Packet captured from 10.0.0.2 to 172.253.63.188 2024-08-07 13:52:02.589 18270-18311 WebFilterVpnService com.example.pbapp D Packet captured from 10.0.0.2 to 216.239.32.116 ...
不过启动VPN后无法加载任何网站,打开网页约一分钟后抛出ERR_NETWORK_CHANGED错误。
我的VPN初始化函数:
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Log.d(TAG, "VPN Service started") val builder = Builder() .addAddress("10.0.0.2", 24) .addRoute("0.0.0.0", 0) .setSession("WebFilterVpnService") val configureIntent = PendingIntent.getActivity( this, 0, Intent(this, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) builder.setConfigureIntent(configureIntent) vpnInterface = builder.establish() if (vpnInterface == null) { Log.e(TAG, "Failed to establish VPN interface") stopSelf() return START_NOT_STICKY } Log.d(TAG, "VPN interface established") startCapture() return START_STICKY }
VPN拦截数据包捕获函数:
private fun startCapture() { vpnInterface?.let { vpnInterface -> executor.execute { Log.d(TAG, "Starting packet capture") val fileInputStream = FileInputStream(vpnInterface.fileDescriptor) val packet = ByteBuffer.allocate(32767) try { while (true) { packet.clear() val length = fileInputStream.read(packet.array()) if (length > 0) { packet.limit(length) if (length >= 20) { val header = ByteArray(20) packet.position(0) packet.get(header, 0, 20) val sourceIP = "${header[12].toInt() and 0xff}.${header[13].toInt() and 0xff}.${header[14].toInt() and 0xff}.${header[15].toInt() and 0xff}" val destinationIP = "${header[16].toInt() and 0xff}.${header[17].toInt() and 0xff}.${header[18].toInt() and 0xff}.${header[19].toInt() and 0xff}" Log.d(TAG, "Packet captured from $sourceIP to $destinationIP") } } } } catch (e: Exception) { Log.e(TAG, "Error during packet capture", e) } finally { fileInputStream.close() } } } ?: run { Log.e(TAG, "VPN interface is null, cannot start packet capture") } }
我还尝试添加DNS服务器:
builder.addDnsServer("8.8.8.8") builder.addDnsServer("1.1.1.1")
但没有效果,请问问题出在哪?
解答
核心问题:只捕获数据包但未转发
Android VPN API的逻辑是:系统把所有流量路由到VPN接口后,你的应用必须负责接收数据包、处理后转发到真实网络,还要把网络返回的响应数据包发回给系统。你的代码目前只做了捕获和日志打印,完全没处理转发,导致所有流量被丢弃,系统收不到响应,最终触发网络错误。需要补充的关键步骤
- 创建输出流处理响应回写:除了读取VPN接口的
FileInputStream,还要获取FileOutputStream,用来把网络响应写回给系统。 - 建立真实网络连接转发数据包:解析捕获到的数据包的目标IP、端口和协议类型,创建对应的Socket连接(TCP用
Socket,UDP用DatagramSocket),把数据包发送到真实网络,同时监听响应并写回VPN输出流。 - 多线程管理连接:单个线程无法同时处理多个连接的转发和响应,建议用线程池管理每个连接的处理任务,避免阻塞。
- 创建输出流处理响应回写:除了读取VPN接口的
修复示例(简化版)
修改startCapture函数,添加基础转发逻辑:
private fun startCapture() { vpnInterface?.let { vpnInterface -> executor.execute { Log.d(TAG, "Starting packet capture and forwarding") val vpnIn = FileInputStream(vpnInterface.fileDescriptor) val vpnOut = FileOutputStream(vpnInterface.fileDescriptor) val packet = ByteBuffer.allocate(32767) try { while (true) { packet.clear() val length = vpnIn.read(packet.array()) if (length > 0) { packet.limit(length) if (length >= 20) { val header = ByteArray(20) packet.position(0) packet.get(header, 0, 20) val destIP = "${header[16].toInt() and 0xff}.${header[17].toInt() and 0xff}.${header[18].toInt() and 0xff}.${header[19].toInt() and 0xff}" val protocol = header[8].toInt() and 0xff // 处理UDP数据包(示例) if (protocol == 17) { val udpHeader = ByteArray(8) packet.position(20) packet.get(udpHeader, 0, 8) val destPort = ((udpHeader[2].toInt() and 0xff) shl 8) or (udpHeader[3].toInt() and 0xff) val ds = DatagramSocket() val destAddr = InetAddress.getByName(destIP) val sendPacket = DatagramPacket(packet.array(), length, destAddr, destPort) ds.send(sendPacket) // 接收响应并写回VPN val responseBuffer = ByteArray(32767) val responsePacket = DatagramPacket(responseBuffer, responseBuffer.size) ds.receive(responsePacket) vpnOut.write(responseBuffer, 0, responsePacket.length) ds.close() } } } } } catch (e: Exception) { Log.e(TAG, "Error during packet processing", e) } finally { vpnIn.close() vpnOut.close() } } } ?: run { Log.e(TAG, "VPN interface is null, cannot start capture") } }
- 额外注意事项
- 权限:确保已申请
android.permission.INTERNET和android.permission.BIND_VPN_SERVICE,且Manifest中VPN服务声明时绑定了对应权限。 - TCP处理:TCP转发逻辑更复杂,需要维护连接状态、处理握手和断开,建议参考成熟的VPN实现框架简化开发。
- DNS处理:如果要监控DNS请求,需单独处理UDP 53端口的数据包,解析查询内容后再转发或自行解析。
- 权限:确保已申请
内容的提问来源于stack exchange,提问作者Powplowdevs
相关产品推荐
相关产品推荐

