参考了 surge 的说明文档,qx 应该用的是方法 2 ,需要对协议栈重组,会有额外的性能开销。
https://manual.nssurge.com/book/understanding-surge/cn/在 macOS 和 iOS 下,要想使程序发出的网络连接被另一个程序所接管,而不是直接将数据发送到物理网卡,有以下三种方式:
配置代理:如果系统配置了代理服务器,那么程序在执行网络请求的时候,就不会直接连接目标服务器,而是产生一个发向代理服务器的连接。利用这个特性,可以在本地启动一个代理服务,并配置系统代理为 127.0.0.1 (即本机)的一个端口,这样就可以接管网络请求。
但是,这种方式要求程序自身支持代理机制,系统的代理设置只是告知程序应该使用代理,需要程序自己完成代理的后续逻辑。好在,对于绝大部分带用户界面的程序,由于开发时使用了系统的高层网络框架( Cocoa/Cocoa Touch ),开发者不需要进行任何额外的工作就可以支持代理。
而对于命令行程序,由于使用的是 POSIX 接口进行网络请求,该接口并没有对代理服务器提供内嵌支持,所以需要开发者自己完成对代理服务器的支持,这导致各种命令行程序对代理的支持情况和具体行为并不统一。同时由于大部分命令行程序并没有为 macOS 进行特殊处理,所以不会理会系统配置里的代理服务器设置。大部分命令行程序需要通过环境变量 https_proxy 和 http_proxy 去配置代理,还有一部分需要通过修改配置文件进行配置。
还有少量程序由于完全缺乏代理服务器的支持,无法通过这种方式去接管网络连接。
虚拟网卡( Virtual Network Interface ,简写为 VIF ):主流操作系统几乎都存在 TUN 和 TAP 两种虚拟网卡接口,原本是为了提供对 VPN 的支持。通过在系统中建立虚拟网卡并配置全局路由表,可以接管所有的网络请求。
这种方式对应程序来说是无感知的,所以并不需要程序主动提供支持,几乎所有程序都可以被这种方式接管网络请求。除非程序主动指定了物理网卡,绕过了默认的虚拟网卡。
Socket Filter:这是 macOS 的一项内核特性,可以通过注入一个 Kernel Extension ( kext )对所有 socket 调用进行 hook ,以此接管请求。
除系统自身的一些程序外,这种方式可以强制接管系统中所有程序的所有网络请求。如 Proxifier 和 Little Snitch 就使用了这种方式接管网络。
这三种方式各有优劣:
方法 1 性能最优,对系统侵入性最小,无奈有部分程序不支持。
方法 2 性能略低,因为截取到的流量是 IP 层的数据包,需要有一个 TCP 协议栈进行重组装,造成了额外的性能开销。
方式 3 最暴力,对系统侵入性高,Kernel Extension 有可能造成整个系统的不稳定,Apple 已确认在未来的 macOS 中将取消对 Socket Filter 的支持。