很多人第一次接触 TCP 时,会把「三次握手」「四次挥手」背成口诀。真正写服务端、调超时、抓包排障时,口诀往往不够用——更稳妥的方式,是顺着连接状态机去问:每一步交换在消除哪一类不确定性?

三次握手在消除什么

建立连接时,双方都需要确认两件事:对端可达,以及对端的初始序列号(ISN)已被对方知晓。这就对应经典的三步:

  1. SYN:客户端携带自己的 ISN,进入 SYN_SENT
  2. SYN+ACK:服务端确认客户端 ISN,并带上自己的 ISN,进入 SYN_RCVD
  3. ACK:客户端确认服务端 ISN,双方进入 ESTABLISHED

两次不够,是因为服务端发出的 ISN 还没被确认;四次通常没必要,因为第二步可以把「确认对方」与「宣告自己」合并。理解到这一层,就更容易解释为何存在「连接请求延迟到达导致的伪连接」这类历史问题,以及现代实现如何用时间戳、ISN 随机化等方式加强防护。

挥手为何常是四次

TCP 连接是全双工的。一方说「我不再发送」,并不等于另一方也立刻无数据可发。因此关闭流程天然偏向:

  • 主动关闭方发送 FIN,进入 FIN_WAIT_1
  • 对端先 ACK 该 FIN,进入 CLOSE_WAIT(半关闭:仍可发送);
  • 对端应用层也关闭后,再发自己的 FIN
  • 主动方 ACK,再经历 TIME_WAIT 后真正释放。

当第二、三步被合并(对端立刻 FIN+ACK),表面上会变成「三次挥手」,但语义仍是两次方向上的关闭宣告。

TIME_WAIT 不是累赘,是保险

TIME_WAIT 存续约 2×MSL,主要保护两类风险:旧连接的重复报文干扰新连接;以及最后一个 ACK 丢失时对端重传 FIN 仍能被正确应答。高并发短连接场景里,它会占用端口资源,工程上常见对策是连接复用、调整可复用端口范围,而不是简单粗暴地关闭 TIME_WAIT——后者往往把偶发诡异问题推到更晚才暴露。

研记要点:把握手/挥手当成「对齐序列号与关闭意图」的协议对话,而不是固定拍数的仪式。