TCP 三次握手图解:为什么不是两次也不是四次?

面试官最爱问的 TCP 经典三问之一,就是:为什么三次握手要三次,而不是两次或四次? 很多人能背出"客户端发 SYN、服务端回 SYN+ACK、客户端再回 ACK"的流程,但被追问"少一次会怎样、多一次行不行"时就开始转圈。 这篇笔记蟹老板带你把背后的原因扒一遍。

先回顾:三次握手到底干了啥

三次握手的目标只有一个:让两端都确认对方的"收信能力"和"发信能力"都正常, 并在此过程中交换好双方的初始序列号(ISN)。简化时序图如下:

   Client                          Server
     |                                |
     |   1) SYN, seq=x                |
     | -----------------------------> |
     |                                |
     |   2) SYN, seq=y, ACK=x+1       |
     | <----------------------------- |
     |                                |
     |   3) ACK, seq=x+1, ACK=y+1     |
     | -----------------------------> |
     |                                |
   ESTABLISHED                    ESTABLISHED

为什么必须是三次?

从两个角度可以解释清楚。

角度一:双向能力确认

三次握手让两端各自完成一次"我能发 + 我能收"的闭环:

  • 第 1 次:服务端确认了"客户端能发"。
  • 第 2 次:客户端确认了"服务端能收 + 服务端能发"。
  • 第 3 次:服务端确认了"客户端能收"。

少一次就缺一个证据。如果只两次,服务端永远不知道客户端有没有收到自己回的 SYN+ACK, 就只能盲目地认为连接已经建好 —— 这会带来下面的"幽灵连接"问题。

角度二:防止旧的重复 SYN 建立无效连接

假设客户端发了一个 SYN,因网络拥堵长时间没到,于是它重发了一个新的,新连接走完正常流程后关闭。 然后那个迟到的旧 SYN 终于晃到了服务端:

  • 如果只有两次握手:服务端一收到 SYN 立刻进入 ESTABLISHED, 然后傻等数据 —— 但客户端早就不打算用这个连接了,资源就被白白占住。
  • 三次握手时:服务端虽然回了 SYN+ACK,但还在等客户端的最后一次 ACK。 客户端发现这是个旧连接对应的 SYN+ACK(序列号对不上),会回一个 RST 让服务端撤销,资源就回收了。
一句话总结:第三次 ACK 给了客户端"反悔"的机会

那四次行不行?

理论上当然可以,但没必要。第 2 次握手里,服务端把"SYN"和"ACK"合在一个包里发了出去。 如果硬要拆成两次(先 ACK 客户端的 SYN,再发自己的 SYN),就变成四次握手 —— 浪费了一次往返时间,对建立连接的语义没有任何提升。TCP 协议是讲性价比的协议。

小细节:ISN 为什么要随机?

初始序列号不是从 0 开始,而是基于时钟随机生成(RFC 793 / RFC 6528)。这样做有两个好处:

  1. 避免旧连接的数据被新连接误收:序列号空间隔得足够远,撞车概率极低。
  2. 抗 SYN 攻击 / 抗序列号预测:攻击者更难伪造合法序列号。

用代码看一眼

Linux 下用 strace 跟踪一个 nc 连接,能直观看到三次握手对应的系统调用:

$ strace -e trace=network nc 127.0.0.1 8080
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(8080),
            sin_addr=inet_addr("127.0.0.1")}, 16) = 0
# connect() 返回 0 时,三次握手已在内核中完成

用户态只看见一个 connect() 调用 —— 真正的三次握手由内核 TCP 协议栈搞定。 这也是为什么"性能优化"时大家盯着的是内核参数(如 tcp_syn_retriestcp_max_syn_backlog), 而不是用户态代码。

划重点

  • 三次握手 = 双向能力确认 + 防御旧连接。
  • 两次不够:服务端无法确认客户端的接收能力,且容易被旧 SYN 拖死。
  • 四次多余:第 2 次握手已经把 ACK 与 SYN 合并了。
  • ISN 随机化是顺手做的安全加固,别忘了它的存在。

下一篇蟹老板会讲四次挥手与 TIME_WAIT,把 TCP 连接的"善终"也讲明白。