什么是 TCP Fast Open (TFO)
众所周知,TCP 连接需要进行三次握手方可开始传输数据。这使得每个 TCP 请求需要浪费掉一个 RTT 去建立连接。TCP 协议被发明时,网络还处于 LAN 局域网时代,RTT 只有毫秒级,所以这个设计并没有问题。
而到了互联网与无线网络时代,RTT 通常可高达几十上百毫秒。这时被浪费掉的这一个 RTT 便成为了一个问题。TCP Fast Open 也因此而生。TFO 的具体实现方式简单来说,就是在三次握手时的第一个 SYN 包里,附带上所需要传输的第一个数据包,如 TLS 协议的 Client Hello。当握手完成后,就认为服务端已确认收到第一个数据包,开始进行后续的传输。这样就挽回了握手延迟的 RTT 损失。(具体实现上还有诸如 cookie 等细节,这里不再展开)
兼容性问题
但是这项改进会遇到一个问题,由于这是一项对 TCP 协议的扩展,中间网络设备有可能不支持该特性。比方说部分防火墙(NAT)会直接将带有数据段的 SYN 包认为是非法数据包直接抛弃。导致使用 TFO 的话就完全无法建立连接。(如中国的蜂窝数据网络)
为此,操作系统不得不额外引入一个 blackhole 机制,当对某 IP 以 TFO 进行握手时,如果在特定时间内都没有收到回应,那么就重新尝试以非 TFO 方式进行握手,如果成功,则将该 IP 加入黑名单,之后再也不以 TFO 进行握手。
但这个机制有两个明显的问题:
- 可能因为网络正常波动丢包而误判,导致 IP 进入黑名单,后续连接全部丧失 TFO 特性。
- 有的时候是在特定网络下 TFO 无效(如数据网络),操作系统的黑名单只记录了目标 IP,所以在切换网络后,依然无法使用 TFO。
在我们的测试中,使用一段时间后,代理服务器 IP 几乎一定会被加入黑名单。
这里有两个关于 macOS 的 Tips:
- 可使用 sudo sysctl -w net.inet.tcp.clear_tfocache=1 命令,强行清空系统的黑名单。
- 可使用 sudo sysctl -w net.inet.tcp.disable_tcp_heuristics=1,强行关闭黑名单功能。(但同时还会影响 ECN 与 MPTCP)
最新版本 Surge 的改进
我们在最新版本的 Surge iOS (Build 2156)和 Mac (Build 1590)中,为解决该问题做了很多优化:
既然自动的黑名单机制基本导致 TFO 功能失效了,不妨改为手动管理 TFO。[SSID Setting] 增加了新的配置:tfo-behaviour
[SSID Setting]
"My Home" tfo-behaviour=force-enabled
目前有三个参数:
auto:与先前版本一致,不进行额外干预。
force-enabled:无视系统黑名单,始终使用 TFO 进行握手。
force-disabled:关闭所有策略的 TFO 功能。
可使用此参数,在已经测试过 TFO 有效的网络下,确保连接不会被黑名单机制影响。
在请求的 Note 中,将展示更多的 TFO 细节信息,例如:
TCP Fast Open was successful (tfo_syn_data_sent, tfo_syn_data_acked)
表示 TFO 已成功。
Attempted to use TCP Fast Open but failed (tfo_heuristics_disable)
表示由于被加入黑名单,TFO 已失败。
Attempted to use TCP Fast Open but failed (tfo_cookie_req, tfo_no_cookie_rcv)
表示服务端未开启 TFO 支持。
SecureTransport 和 OpenSSL 两种 TLS 引擎下,TLS 类型的代理也支持使用 TFO 进行握手了。
Tips:可在 Surge Mac Dashboard 的顶部标签栏右键,勾选 Setup 栏目,以快速查看连接的建立速度: