我正在尝试在为 Android 创建防火墙/过滤应用程序时做类似的事情。我在这中间,所以用适当大小的盐粒来接受我所说的话。:-)
您/我的情况的问题是您正在从 VpnService 提供给您的虚拟网络接口中取出数据包,但随后您正在取出这些数据包并使用套接字将它们写出。套接字旨在处理客户端和服务器之间的应用程序有效负载的传输,而不是数据包。
套接字将获取您提供的任何数据,并将该数据包装在它自己创建的数据包中(如果使用 Socket,则为 TCP,如果使用 DatagramSocket,则为 UDP)。在您的情况下,您正在处理套接字的数据本身就是一个数据包,因此您最终会得到一个数据包中的数据包(可能是 UDP 数据包中的 TCP 数据包)。
当打包的数据包到达服务器的套接字时,该端的网络接口和 ServerSocket 将打开有效负载并发现它是另一个数据包。可能不会工作,因为从服务器端套接字(例如 Web 服务器)读取的任何内容都需要应用程序有效负载(例如 HTTP 标头/等)。
现在,当您拥有一个真正的 VPN 隧道时,该隧道的服务器端可能会将您的“包装数据包”有效负载从它接收的 UDP 数据包中提取出来,然后将该数据包直接交给可以解释数据包本身的网络接口.
如果没有这个真正的 VPN 隧道,您的 VpnService 实现本质上必须成为一个虚拟网络接口本身,处理 TCP/UDP/等。虚拟网络接口和您的代码之间的协议。基本上,从您的 VPN 接口读取的数据包必须被视为应用程序数据流(数据包整理和重组),然后再写入您的传出套接字。然后,您必须以某种方式确认您刚刚从接口消耗的数据包。然后,您必须从您的 Socket(这是一个有效负载数据流)中获取传入数据,并将其分解为数据包,然后您可以将这些数据包发送回您的 VPN 接口的输出流。最后,您需要处理您的虚拟 VPN 接口为响应您发送的数据包而发送的确认流量。这并非易事。
我真的希望我对这一切都错了,有人有一个用 Java 编写的简单的“虚拟网络接口”,可以用来代替真正的 VPN 隧道。我一直没能找到它。