4、TCP 三次握手:SYN、SYN-ACK、ACK 的抓包验证与握手延迟分析
三次握手,说白了就是 TCP 连接建立的「敲门仪式」。你想想看,两个人要通话,总得先确认对方在听吧?TCP 也一样,客户端和服务端得互相确认「我准备好了,你呢?」。这个过程,就是通过 SYN、SYN-ACK、ACK 这三个报文完成的。
我在项目里见过不少新手,觉得三次握手就是背背状态机。但真正遇到线上问题的时候,你会发现——握手延迟往往是应用卡顿的元凶。今天我们就用 Wireshark 抓包,把这三个报文一个一个揪出来看。
4.1 三次握手的标准流程
先画个图,把流程理清楚。别急着抓包,脑子里得有画面。
4.2 抓包验证:把报文抓出来看看
光看理论不过瘾。我们直接上 Wireshark,抓一个真实的 HTTP 请求看看。
4.2.1 抓包准备
我个人习惯用 tcp.port == 80 作为过滤条件,这样只显示 HTTP 相关的 TCP 流。你也可以用 tcp.flags.syn == 1 直接过滤 SYN 包。
tcp.stream eq 0 可以只看第一个 TCP 流,避免被其他连接干扰。
4.2.2 抓包结果分析
下面是我从一次真实抓包中提取的三次握手报文。注意看 seq 和 ack 的变化。
| 序号 | 时间 | 源 → 目的 | Flags | Seq | Ack |
|---|---|---|---|---|---|
| 1 | 0.000000 | 192.168.1.100 → 93.184.216.34 | SYN | 0 | 0 |
| 2 | 0.045123 | 93.184.216.34 → 192.168.1.100 | SYN-ACK | 0 | 1 |
| 3 | 0.045210 | 192.168.1.100 → 93.184.216.34 | ACK | 1 | 1 |
看到了吗?
- 第一次握手:客户端发 SYN,seq=0。这是相对序列号,Wireshark 默认从 0 开始显示。
- 第二次握手:服务端回 SYN-ACK,seq=0,ack=1。注意 ack 是客户端的 seq+1。
- 第三次握手:客户端发 ACK,seq=1,ack=1。服务端的 seq+1 变成了 ack。
4.3 握手延迟分析:慢在哪里?
三次握手本身很快,通常也就几十毫秒。但一旦出现延迟,问题就来了。我遇到过最夸张的一次,握手花了 3 秒多——用户直接反馈「网页打不开」。
4.3.1 延迟的三大来源
说白了,握手延迟就三个地方:
- 网络 RTT(往返时间):这是物理限制。客户端到服务端距离越远,RTT 越大。比如从北京到纽约,RTT 大约 200ms,三次握手至少 1.5 个 RTT,也就是 300ms。
- 服务端处理延迟:服务端收到 SYN 后,需要分配传输控制块(TCB)。如果服务器负载高,这个分配过程可能变慢。
- 客户端处理延迟:收到 SYN-ACK 后,客户端要回复 ACK。如果客户端 CPU 忙或者 socket 队列满了,也会卡住。
4.3.2 用 Wireshark 计算握手延迟
Wireshark 里有个好东西叫「TCP 握手 RTT」。你选中 SYN 包,在 Statistics → TCP Stream Graph → Round Trip Time Graph 里就能看到。
我一般手动算:
- 从 SYN 发出到收到 SYN-ACK:这是半个 RTT(服务端处理时间 + 网络传输时间)。
- 从 SYN-ACK 发出到收到 ACK:这是另外半个 RTT。
- 两者相加就是完整的握手 RTT。
拿上面的抓包数据来说:
- SYN → SYN-ACK:0.045123 - 0.000000 = 45.123ms
- SYN-ACK → ACK:0.045210 - 0.045123 = 0.087ms
- 总握手时间:45.123 + 0.087 ≈ 45.21ms
嗯,这里要注意:第二次握手到第三次握手的时间极短(0.087ms),说明客户端处理很快。但第一次到第二次花了 45ms,说明网络 RTT 占了主导。
4.4 避坑指南:三次握手的常见问题
做网络排障这么多年,我总结了几条血泪教训:
4.4.1 SYN 超时重传
客户端发 SYN 后,如果没收到 SYN-ACK,会等一段时间(默认 1 秒)后重传。如果重传 3 次还没回应,连接就超时了。
我曾经遇到过一台服务器,SYN 包发出去石沉大海。抓包一看,服务端根本没收到 SYN。最后发现是交换机端口配置了错误的 VLAN,直接把包丢了。
4.4.2 SYN Flood 攻击
攻击者伪造大量 SYN 包,但不完成第三次握手。服务端会一直保留半连接,直到资源耗尽。
应对方法:
- 开启 SYN Cookie(Linux 默认开启)
- 限制半连接队列大小(
net.ipv4.tcp_max_syn_backlog) - 使用防火墙限速
4.4.3 握手延迟的优化思路
如果你的应用对握手延迟敏感,可以考虑:
- TCP Fast Open(TFO):在 SYN 里携带数据,省掉一次 RTT。但需要客户端和服务端都支持。
- 连接复用:用 HTTP Keep-Alive 或连接池,避免频繁握手。
- 部署 CDN:把服务端搬到离用户近的地方,减少 RTT。
4.5 小结
三次握手,说白了就是 SYN、SYN-ACK、ACK 三个报文的来回。抓包验证时,重点看 seq 和 ack 的变化,以及每个阶段的时间差。握手延迟主要来自网络 RTT 和服务端处理,用 Wireshark 的 RTT 图可以快速定位瓶颈。
嗯,这一章就到这里。记住:抓包是手段,不是目的。真正重要的是通过抓包理解 TCP 的行为,然后找到优化方向。