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 的核心机制串起来了。我建议你保存下来,以后排查问题时对着看。

TCP 核心机制总览图 TCP 核心机制总览 三次握手 建立连接 四次挥手 断开连接 序列号/确认号 数据排序与确认 窗口机制 流量控制 拥塞控制 慢启动/拥塞避免 关键排查点 • 三次握手耗时 > 100ms → 检查网络延迟/丢包 • TIME_WAIT 堆积 → 调整 tcp_tw_reuse 或 tcp_max_tw_buckets • 窗口太小 → 增大接收缓冲区(net.core.rmem_max) • 序列号乱序 → 检查中间设备(防火墙/负载均衡)

4.6 抓包实战:一眼看穿问题

最后分享一个我常用的排查思路。拿到一个抓包文件,我会按这个顺序看:

  1. 看三次握手: 过滤 tcp.flags.syn==1,看 SYN 和 SYN+ACK 的时间差。如果超过 1 秒,大概率是丢包。
  2. 看窗口大小: 过滤 tcp.window_size < 65535,看有没有窗口缩得太小的情况。
  3. 看重传: 过滤 tcp.analysis.retransmission,重传率超过 2% 就得重视了。
  4. 看零窗口: 过滤 tcp.window_size == 0,如果频繁出现,说明接收方处理不过来。

我的习惯: 在 Wireshark 里把「TCP 分析」的着色规则打开。绿色是正常,红色是重传,黄色是乱序。一眼扫过去,哪里有问题一目了然。

TCP 协议看着复杂,但核心就这几个点。你只要把三次握手、四次挥手、序列号确认号、窗口机制搞明白了,80% 的网络性能问题都能定位。剩下的,就是多抓包、多看了。