很多人第一次接触 TCP 时,会把「三次握手」「四次挥手」背成口诀。真正写服务端、调超时、抓包排障时,口诀往往不够用——更稳妥的方式,是顺着连接状态机去问:每一步交换在消除哪一类不确定性?
三次握手在消除什么
建立连接时,双方都需要确认两件事:对端可达,以及对端的初始序列号(ISN)已被对方知晓。这就对应经典的三步:
- SYN:客户端携带自己的 ISN,进入
SYN_SENT。 - SYN+ACK:服务端确认客户端 ISN,并带上自己的 ISN,进入
SYN_RCVD。 - 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——后者往往把偶发诡异问题推到更晚才暴露。
研记要点:把握手/挥手当成「对齐序列号与关闭意图」的协议对话,而不是固定拍数的仪式。