TCP四次挥手
建立连接需要“三次握手”,而断开连接之所以需要**“四次挥手”,根本原因在于 TCP 协议是全双工通信**(也就是双方可以同时、独立地收发数据)。
#tech / network
#type / concept
#status / seed
TCP 四次挥手详解
一、四次挥手流程(以客户端主动关闭为例)
| 步骤 | 发送方 | 报文类型 | 状态变化 | 关键说明 |
|---|---|---|---|---|
| 第一次挥手 | 客户端 | FIN (Seq=u) | ESTABLISHED → FIN_WAIT_1 | 客户端不再发送数据,但可接收 |
| 第二次挥手 | 服务端 | ACK (Ack=u+1) | CLOSE_WAIT | 服务端可能仍有数据待发送 |
| 第三次挥手 | 服务端 | FIN (Seq=w) | CLOSE_WAIT → LAST_ACK | 服务端数据发送完毕 |
| 第四次挥手 | 客户端 | ACK (Ack=w+1) | FIN_WAIT_2 → TIME_WAIT | 客户端等待 2MSL 后关闭连接 |
1. 第一次挥手(客户端 -> 服务端)
- 动作:客户端的数据发送完毕了,打算断开连接。它会向服务端发送一个带有
FIN=1标志位的数据包,并带上自己的序列号seq = u。发送后,客户端进入FIN_WAIT_1状态。 - 潜台词:“服务端你好,我这边的数据全部发完了,我要关闭发送通道了。”
- 注意:此时客户端只是不能发数据了,但仍然可以收数据(这叫半关闭状态)。
2. 第二次挥手(服务端 -> 客户端)
- 动作:服务端收到了客户端的
FIN包,立刻回复一个带有ACK=1标志位的确认包,确认号ack = u + 1,序列号seq = v。服务端进入CLOSE_WAIT状态;客户端收到后进入FIN_WAIT_2状态。 - 潜台词:“收到!但我可能还有些剩余的数据没传完,你先等我一下,我发完了再告诉你。”
- 注意:为什么这里不直接连着服务端的
FIN一起发(变成三次挥手)?就是因为服务端此时可能还有业务数据在缓冲区没有发送完毕,必须先把剩下的数据传完。
3. 第三次挥手(服务端 -> 客户端)
- 动作:等服务端把剩下的数据也全部发送完毕后,它也打算关闭发送通道了。于是向客户端发送一个带有
FIN=1和ACK=1标志位的数据包,序列号seq = w。发送后,服务端进入LAST_ACK(最后确认)状态。 - 潜台词:“好了,我这边的数据也彻底发完了,我的发送通道也可以关闭了。”
4. 第四次挥手(客户端 -> 服务端)
- 动作:客户端收到了服务端的
FIN包,必须给服务端一个交代,于是回复一个带有ACK=1标志位的确认包,确认号ack = w + 1。服务端收到这个确认包后,立刻进入CLOSED状态,彻底关闭连接。 - 潜台词:“收到!那你关吧,我也准备彻底关了。”
- 高频考点(TIME_WAIT):客户端发送完第四次挥手的
ACK后,并不会立刻关闭,而是会进入一个极其特殊的TIME_WAIT状态,必须等待 2MSL(最大报文段生存时间,约等于 1 到 4 分钟)后,才会真正进入CLOSED状态。
注意:
- 主动关闭方会进入
TIME_WAIT状态,被动关闭方直接进入CLOSED状态。- 实际抓包中第一次挥手可能是
FIN+ACK(因 TCP 头部 ACK 字段默认有效)。
二、为什么需要四次挥手?
- 全双工特性:TCP 连接双向独立关闭,服务端需等待数据发送完毕后再发送
FIN。 - 可靠性保证:若合并第二次和第三次挥手(
ACK+FIN),可能导致客户端误判FIN丢失而重复重传。
三、关键状态解析
1. TIME_WAIT(2MSL 等待)
- 作用:
- 确保最后一个
ACK到达服务端(若丢失,服务端会重传FIN)。 - 消除网络中残留的旧报文,避免干扰新连接。
- 确保最后一个
- 默认时长:Linux 中为 60 秒(2×MSL),可通过
tcp_fin_timeout调整。
2. CLOSE_WAIT
- 触发条件:被动关闭方收到
FIN但未调用close()。 - 风险:若程序未及时关闭连接,会导致连接长期滞留(理论无限长)。
四、异常处理
| 挥手丢失步骤 | 处理机制 | 重传控制参数 |
|---|---|---|
| 第一次挥手 | 客户端重传 FIN,超时后直接关闭 | tcp_orphan_retries |
| 第二次挥手 | 客户端重传 FIN(服务端 ACK 不重传) | tcp_orphan_retries |
| 第三次挥手 | 服务端重传 FIN,客户端在 FIN_WAIT_2 状态超时(默认 60 秒)后关闭 | tcp_fin_timeout |
| 第四次挥手 | 服务端重传 FIN,客户端在 TIME_WAIT 状态持续 2MSL 后关闭 | - |
注:若程序崩溃,系统会强制发送
RST终止连接。
五、优化建议
- 减少
TIME_WAIT:- 启用
net.ipv4.tcp_tw_reuse(需时间戳支持)。 - 避免短连接高并发场景(如改用长连接)。
- 启用
- 排查
CLOSE_WAIT:检查代码是否漏调close()或存在阻塞。
Related notes
- 所属 MOC: 计算机网络MOC