★ TCP 为什么要三次握手、四次挥手?状态如何流转?
结论先行:三次握手的目的是双向确认收发能力并同步初始序列号,同时让收到“历史失效报文”的一方有机会拒绝旧连接;四次挥手是因为 TCP 是全双工,两个方向要各自独立关闭——被动方收到 FIN 后可能还有数据要发,所以“回 ACK”和“发自己的 FIN”必须分成两步。
三次握手(建立连接)
1. Client → Server: SYN, seq=x
2. Server → Client: SYN+ACK, seq=y, ack=x+1
3. Client → Server: ACK, ack=y+1
- 状态流转:Client:CLOSED → SYN_SENT → ESTABLISHED;Server:LISTEN → SYN_RCVD → ESTABLISHED;
- 为什么不是两次:两次只能证明 Server 收发正常、Client 能发,无法证明 Client 能收,也防不住历史连接误建;
- 为什么不是四次:三次已能完成双向确认,第四次纯属冗余。
四次挥手(断开连接)
1. 主动方 → 被动方: FIN 主动方进入 FIN_WAIT_1
2. 被动方 → 主动方: ACK 主动方 FIN_WAIT_2;被动方 CLOSE_WAIT
3. 被动方 → 主动方: FIN 被动方 LAST_ACK(数据发完后再发 FIN)
4. 主动方 → 被动方: ACK 主动方 TIME_WAIT(等 2MSL)→ CLOSED
- 第 2、3 步之间被动方仍可发数据,这段叫“半关闭”;
- 主动方最后等 2MSL:一是让迟到的旧报文在网络中消亡,避免污染新连接;二是若最后的 ACK 丢失,还能响应对端重传的 FIN。
编程视角
Socket socket = new Socket("example.com", 443); // 建连(隐含三次握手)
// 双向收发数据……
socket.close(); // 断开(触发四次挥手)
常见追问 / 记忆点
- 追问:TIME_WAIT 过多会怎样?答:占用端口与内存,高并发短连接下可能端口耗尽;解法是长连接/连接复用或调内核参数。
- 追问:挥手能三次吗?答:只有被动方“确认关闭”和“数据发完”恰好同时满足才能合并,一般场景做不到。
- 记忆点:握手三次为“双向确认 + 同步序列号 + 防历史报文”;挥手四次是“全双工各自关闭”的必然结果。