4. TCP协议分析:三次握手、四次挥手、序列号与确认号、窗口机制
TCP 协议,说白了就是网络通信里的「快递员」。它得保证你发的每个包裹都完整送到,而且顺序不能乱。我做了这么多年性能优化,见过太多问题其实就出在 TCP 的这几个核心机制上。今天咱们就把它掰开揉碎了讲清楚。
4.1 三次握手:建立连接的门槛
客户端和服务器要通信,得先「握手」三次。这可不是客套,是双方确认彼此都在线、都能收发数据。
核心流程:
- 第一次握手(SYN): 客户端发送 SYN 包,告诉服务器「我想跟你说话」。此时客户端进入 SYN_SENT 状态。
- 第二次握手(SYN+ACK): 服务器收到后,回复 SYN+ACK,意思是「收到,我也准备好了」。服务器进入 SYN_RCVD 状态。
- 第三次握手(ACK): 客户端再发一个 ACK,确认「好的,开始吧」。双方进入 ESTABLISHED 状态。
我遇到过一个问题:某次线上服务响应特别慢,抓包一看,三次握手花了整整 3 秒。为什么?因为客户端发的 SYN 包丢了,重试了 3 次才成功。嗯,这里要注意,SYN 超时重试的间隔是 1 秒、2 秒、4 秒,如果网络不稳定,光握手就能把人急死。
我的习惯: 抓包时先看三次握手的耗时。如果超过 100ms,就得排查网络延迟或丢包了。用 Wireshark 的「TCP Handshake」过滤器,一眼就能看出问题。
4.2 四次挥手:优雅地告别
断开连接比建立连接更复杂。因为双方可能还有数据没发完,得等对方说「我没事了」才能关。
核心流程:
- 第一次挥手(FIN): 主动方发送 FIN,表示「我没数据要发了」。进入 FIN_WAIT_1 状态。
- 第二次挥手(ACK): 被动方回复 ACK,表示「知道了,但我可能还有数据要发」。主动方进入 FIN_WAIT_2 状态。
- 第三次挥手(FIN): 被动方发完数据后,发送 FIN,表示「我也没数据了」。进入 LAST_ACK 状态。
- 第四次挥手(ACK): 主动方回复 ACK,然后进入 TIME_WAIT 状态,等待 2MSL 后关闭。
你想想看,为什么要有 TIME_WAIT?因为最后一个 ACK 可能丢。如果丢了,被动方会重发 FIN,主动方得能再次回复 ACK。我见过一个案例:服务器上 TIME_WAIT 连接堆积到几万个,导致新连接建不起来。后来调整了 net.ipv4.tcp_tw_reuse 参数才解决。
避坑指南: 我曾经在生产环境直接开了 tcp_tw_recycle,结果 NAT 环境下的用户全连不上。后来才知道这个参数在 NAT 后面会出大问题。现在 Linux 4.12+ 已经把它移除了,但老系统上还是得小心。
4.3 序列号与确认号:数据的「身份证」
TCP 是面向字节流的。每个字节都有一个序列号,用来保证数据不乱序、不重复。
| 字段 | 作用 | 抓包示例 |
|---|---|---|
| Seq(序列号) | 当前数据包的第一个字节的编号 | Seq=1 表示从第 1 个字节开始 |
| Ack(确认号) | 期望收到的下一个序列号 | Ack=1461 表示已收到 1-1460 字节 |
举个例子:客户端发了一个包,Seq=1,Len=1460。服务器回复 Ack=1461,意思就是「前 1460 个字节我收到了,你发第 1461 个吧」。如果服务器收到的是乱序包,比如先收到 Seq=2921 的包,它会回复 Ack=1461,告诉客户端「我还在等 1461,你先把中间的补上」。
实战技巧: 在 Wireshark 里看「TCP Analysis Flags」,如果出现 TCP Dup ACK 或 TCP Retransmission,说明有丢包或乱序。我一般用 tcp.analysis.ack_rtt 来算确认延迟,超过 200ms 就得查查了。
4.4 窗口机制:流量控制的阀门
窗口大小决定了发送方能一口气发多少数据,不用等确认。这玩意儿直接影响吞吐量。
窗口的核心逻辑:
- 接收窗口(rwnd): 接收方告诉发送方「我还有多少缓存空间」。如果 rwnd=0,发送方就得停。
- 拥塞窗口(cwnd): 发送方自己估算的网络容量。刚开始很小,慢慢增大。
- 实际发送量: 取 rwnd 和 cwnd 的最小值。
我记得有一次做性能测试,发现带宽利用率只有 30%。抓包一看,接收窗口只有 64KB,但网络延迟是 50ms。算一下:64KB / 50ms = 1.28MB/s,确实上不去。后来调大了接收缓冲区,窗口变成 256KB,吞吐量直接翻倍。
注意: 窗口缩放因子(Window Scaling)默认是关闭的?其实不是,现在操作系统都开了。但有些老旧设备不支持,会导致窗口最大只有 64KB。抓包时看 Window size scaling factor 字段,如果为 -1,说明没协商成功。
4.5 知识体系总览
下面这张图把 TCP 的核心机制串起来了。我建议你保存下来,以后排查问题时对着看。
4.6 抓包实战:一眼看穿问题
最后分享一个我常用的排查思路。拿到一个抓包文件,我会按这个顺序看:
- 看三次握手: 过滤
tcp.flags.syn==1,看 SYN 和 SYN+ACK 的时间差。如果超过 1 秒,大概率是丢包。 - 看窗口大小: 过滤
tcp.window_size < 65535,看有没有窗口缩得太小的情况。 - 看重传: 过滤
tcp.analysis.retransmission,重传率超过 2% 就得重视了。 - 看零窗口: 过滤
tcp.window_size == 0,如果频繁出现,说明接收方处理不过来。
我的习惯: 在 Wireshark 里把「TCP 分析」的着色规则打开。绿色是正常,红色是重传,黄色是乱序。一眼扫过去,哪里有问题一目了然。
TCP 协议看着复杂,但核心就这几个点。你只要把三次握手、四次挥手、序列号确认号、窗口机制搞明白了,80% 的网络性能问题都能定位。剩下的,就是多抓包、多看了。