TCP 三次握手与四次挥手
TCP 连接建立和关闭的完整流程,面试必考。主线:为什么三次、为什么四次、状态机、异常场景。
三次握手
三次握手: 双方确认收发能力
| 步骤 | 报文 | 证明 |
|---|---|---|
| 1 | SYN(seq=x) | 客户端发送能力 |
| 2 | SYN+ACK(seq=y, ack=x+1) | 服务端收发能力 + 确认收到 |
| 3 | ACK(ack=y+1) | 客户端确认收到 |
为什么不是两次:两次无法确认“客户端的接收能力”(服务端发了 SYN+ACK 但不知道客户端收没收到)。三次是最少次数,让双方都确认对方收发正常。
为什么不是四次:第二次可以合并(SYN+ACK 一起发),三次足够。
序列号的意义:seq 是数据流的起始序号,ack = 对方 seq + 1。序列号让 TCP 能排序、去重、可靠传输(见可靠传输篇)。
四次挥手
四次挥手: 两个方向分别关闭
为什么四次:TCP 是全双工,两个方向独立关闭。主动方发 FIN 只是关闭自己发送方向,被动方可能还有数据要发,所以 ACK 和 FIN 不能合并(和握手的 SYN+ACK 可合并不同)。
状态机关键点
| 状态 | 场景 |
|---|---|
| LISTEN | 服务端等待连接 |
| SYN_SENT / SYN_RCVD | 握手中间态 |
| ESTABLISHED | 正常通信 |
| FIN_WAIT_1/2 | 主动关闭方等待 |
| TIME_WAIT | 主动关闭方停留 2MSL |
| CLOSE_WAIT | 被动关闭方等应用 close |
TIME_WAIT(必考):主动关闭方收到 FIN 回 ACK 后等 2MSL(报文最大生存时间 × 2):
- 保证末尾的 ACK 能到达(丢了对方重发 FIN,还能响应)
- 保证旧连接报文在网络中消失,不污染新连接
- 代价:大量短连接时 TIME_WAIT 堆积占端口(高并发服务要处理,见下)
CLOSE_WAIT 堆积:被动关闭方收到 FIN 但应用没调用 close(代码 bug),连接泄漏。netstat 看到大量 CLOSE_WAIT 就是应用没关连接。
常见问题
| 现象 | 原因 |
|---|---|
| 大量 TIME_WAIT | 短连接服务端主动关闭(配 keep-alive 减少) |
| 大量 CLOSE_WAIT | 应用未 close(代码 bug) |
| 半连接队列满 | SYN 洪水 / 并发太高,listen backlog 调优 |
| 连接超时 | 防火墙丢包、对端不响应(SYN 重试) |
面试追问
- 为什么三次握手? 最少次数让双方确认对方收发正常。两次确认不了客户端接收能力
- 为什么四次挥手? 全双工:两个方向独立关。被动方 ACK 和 FIN 不能合并(可能还有数据)
- TIME_WAIT 为什么等 2MSL? 保证末尾 ACK 可达(防重发 FIN)和旧报文消失(防污染新连接)
- CLOSE_WAIT 多说明什么? 应用收到 FIN 没 close:连接泄漏 bug。排查代码
- 握手丢包怎么办? 超时重传 SYN/SYN+ACK(指数退避)。连接超时通常是对端或防火墙问题