👋 TCP 四次挥手:连接是怎么断开的
FIN → ACK → FIN → ACK,全双工独立关闭。为什么是四次?TIME_WAIT 又为什么等 2MSL?
四次挥手在干什么
🎯 终止连接
TCP 四次挥手是终止连接的过程。
因为 TCP 是全双工通信,两个方向要各自独立关闭发送通道,所以需要四次报文交换。
🔑 记忆口诀
FIN → ACK → FIN → ACK
四步挥手不乱来,全双工独立关闭。
完整流程
为什么是四次,不是三次
🔀 全双工要独立关
第一次挥手关闭了主动方的发送通道,但被动方可能还有数据要发。
所以 ACK 和 FIN 要分开发送:先回 ACK 告诉「收到你的 FIN」,等服务端数据发完了再发 FIN。
这就多出一次,变成四次。
🔄 什么时候能三次
如果被动方也没有数据要发,理论上可以合并成三次挥手。
TCP 的延迟确认机制可能让这种情况发生(ACK 和 FIN 一起发)。
TIME_WAIT 为什么要等 2MSL
⏳ TIME_WAIT 的意义
主动关闭方收到对方 FIN、回 ACK 后进入 TIME_WAIT,持续 2MSL(最大报文生存时间的两倍,约 2-4 分钟)。
两个目的:
① 保证最后一个 ACK 被对方收到(若丢失对方会重传 FIN)
② 保证旧连接报文在网络中消失,不干扰新连接。
⚠️ 高并发的问题
TIME_WAIT 会占用端口资源。
高并发服务端可能出现大量 TIME_WAIT,端口耗尽。
优化:SO_REUSEADDR 重用端口、用连接池、调整 tcp_tw_reuse。
TIME_WAIT / CLOSE_WAIT 排查
大量 TIME_WAIT
主动关闭方常见,高并发下端口耗尽。开启 SO_REUSEADDR、用连接池、调整 tcp_tw_reuse。注意 tcp_tw_recycle 在 Linux 4.12 已移除。
大量 CLOSE_WAIT
被动关闭方代码问题——通常没调用 close() 或没正确处理 socket 异常。CLOSE_WAIT 过多 → 查代码连接释放逻辑。
同时关闭(三次)
双方同时发 FIN、各回 ACK,只需三次报文交换即可关闭。
TCP 四次挥手 = FIN → ACK → FIN → ACK,因为全双工要独立关闭两个方向(被动方可能还有数据要发,所以 ACK 和 FIN 分开发)。主动方最后进入 TIME_WAIT(2MSL):保证最后一个 ACK 到达、让旧报文在网络中消失。排查:大量 TIME_WAIT 用 SO_REUSEADDR/连接池;大量 CLOSE_WAIT 一般是代码没 close()。
假设客户端主动关闭。第一次客户端发 FIN 表示数据发完,进入 FIN_WAIT_1;第二次服务端回 ACK,进入 CLOSE_WAIT,客户端进 FIN_WAIT_2,这时候服务端可能还有数据要发;第三次服务端数据发完,发 FIN 进 LAST_ACK;第四次客户端回 ACK,进入 TIME_WAIT 持续 2MSL,服务端收到后 CLOSED,TIME_WAIT 结束后客户端也 CLOSED。
因为 TCP 是全双工的,两个方向的连接要独立关闭。第一次挥手只关闭了主动方的发送通道,但被动方可能还有数据要发,所以它不能立刻关闭,而是先回 ACK 表示收到你的 FIN,等它自己的数据发完了再发 FIN。这样 ACK 和 FIN 分开发,就多了一次变成四次。如果被动方也没数据要发,理论上可以合并成三次。
TIME_WAIT 是主动关闭方在收到对方 FIN、回完 ACK 后进入的状态,持续 2MSL。两个目的:一是保证最后一个 ACK 能被对方收到,如果 ACK 丢了对方会重传 FIN,而 TIME_WAIT 期间客户端还能再回 ACK;二是保证旧连接的所有报文在网络中消失,不干扰后续的新连接。2MSL 是因为 MSL 是最大报文生存时间,2MSL 能覆盖 FIN 重传和 ACK 到达的最长路径。