TCP Fast Open 能省多少握手时间?实测

TCP Fast Open(TFO)经常被描述成「省一次握手」,听起来很美好,实际收益却经常测量不出来。原因很简单:它省的是一次往返时间(RTT),而一次 RTT 在不同网络里差别巨大——同城 5 ms,跨境 200 ms,收益相差 40 倍。

它到底省了什么

标准 TCP 三次握手是这样:

客户端                   服务端
  |------ SYN ------------>|
  |<----- SYN + ACK -------|
  |------ ACK ------------>|   之后才能发数据

开 TFO 之后,客户端可以在 SYN 包里直接带上数据(靠一个 Cookie 证明身份),服务端在 SYN-ACK 里就回数据。对重复连接来说,等于把「建连 + 首字节」压缩成一个往返。

注意关键词是重复连接:第一次连陌生服务器没有 Cookie,仍然走标准握手。所以 TFO 对「大量短连接、且目标相对固定」的场景有效,对长连接、大文件下载几乎无感。

Linux 上怎么开

客户端和服务端都要开,缺一不可:

# 1 只对客户端生效;2 只对服务端生效;3 两者都开
sysctl -w net.ipv4.tcp_fastopen=3

# 永久生效
echo "net.ipv4.tcp_fastopen=3" >> /etc/sysctl.conf
sysctl -p

验证:

cat /proc/sys/net/ipv4/tcp_fastopen

还要注意,应用层必须主动使用 TFO 的 sendmsg 标志(服务端)和 connect 选项(客户端)。内核开了但程序没用,等于没开。curl 7.49+ 和部分语言运行时支持,浏览器各平台的支持情况不一致。

实测大概能省多少

在同一台机器上对同一目标做 20 次短连接,取首字节时间的中位数,量级大致是:

网络 RTT未开 TFO开 TFO差值
同城 5–10 ms~35 ms~28 ms5–8 ms
跨省 30–40 ms~95 ms~58 ms~30 ms
跨境 150–200 ms~450 ms~260 ms~180 ms

表格里的绝对值受环境影响很大,但差值规律稳定:省下的就是一个 RTT 上下。所以在跨境、高延迟链路上,TFO 的收益才真正显现;在局域网和同城,用户根本感知不到。

为什么很多人开了没感觉

  • 连接不是重复的:每次都是新目标,Cookie 用不上。
  • 应用没调用 TFO 接口:内核参数开了,程序不用它。
  • 中间设备丢了 TFO 选项:部分防火墙、NAT 设备会剥离未知的 TCP 选项。
  • 瓶颈不在建连:如果是带宽或 DNS 慢,省一个 RTT 对总时长影响微乎其微。

判断方法:用 ss -i 看连接详情,或者抓包看 SYN 包里有没有 TFO 选项(Kind=34)。没有就是没生效,别怀疑网络。

和 QUIC / 0-RTT 的关系

QUIC 把传输层放到了 UDP 上,建连和 TLS 合并,天然支持 0-RTT。也就是说,在支持 QUIC 的场景里,TFO 解决的问题已经被更彻底地解决了。TFO 的价值主要在那些仍然走 TCP 的老服务和系统级连接上。

💡 判断要不要折腾:先量一下你的平均 RTT。如果主要用户在同城、RTT 小于 20 ms,TFO 的收益在 10 ms 级别,优先级应该排在带宽和 DNS 之后;如果链路跨境、RTT 超过 100 ms,省下的 100–200 ms 对首屏体验是实打实的。

延伸阅读:传输协议与延迟的关系、节点优选方法、速度上不去怎么排查。