TCP 三次握手、四次挥手与 TIME_WAIT、CLOSE_WAIT
一、TCP 三次握手
TCP 是面向连接、可靠的、基于字节流的传输层协议。
客户端 服务端
| |
|----------- SYN ------------>| ① 请求建立连接
| |
|<-------- SYN + ACK ---------| ② 确认请求,同时同步序列号
| |
|----------- ACK ------------>| ③ 确认连接建立
| |
ESTABLISHED ESTABLISHED三次握手的作用是同步初始序列号、确认双方能够建立连接,并避免历史失效的连接请求造成错误连接。
二、TCP 四次挥手
假设客户端主动关闭 TCP 连接,服务端被动关闭。
客户端(主动关闭) 服务端(被动关闭)
| |
ESTABLISHED ESTABLISHED
| |
|------------- FIN ------------->| ① 请求关闭
| |
FIN_WAIT_1 |
| |
|<------------ ACK --------------| ② 确认收到
| |
FIN_WAIT_2 CLOSE_WAIT
| |
| 等待应用关闭连接
| |
|<------------ FIN --------------| ③ 服务端请求关闭
| |
| LAST_ACK
| |
|------------- ACK ------------->| ④ 最后确认
| |
TIME_WAIT CLOSED
|
等待 2MSL
|
CLOSED为什么通常需要四次挥手?
因为服务端收到 FIN 后,可能还有数据需要发送,因此需要先回复 ACK,等数据发送完毕后,再发送 FIN。
如果服务端立即准备关闭,也可能将 ACK 和 FIN 合并发送。
三、CLOSE_WAIT
CLOSE_WAIT 是被动关闭方收到 FIN 并确认后,等待本地应用关闭连接的状态。
常见原因:
Socket 没有正确关闭。
Java 异常分支遗漏资源释放。
Netty EventLoop 被阻塞。
开启 TCP 半关闭后,没有正确完成关闭流程。
Linux 查看:
ss -tanp state close-waitCLOSE_WAIT 长时间大量堆积,应重点检查应用连接关闭逻辑。
四、TIME_WAIT
TIME_WAIT 是主动关闭方发送最后一个 ACK 后进入的等待状态。
为什么等待 2MSL?
最后一个 ACK 可能丢失,需要能够处理服务端重传的 FIN。
避免旧连接中的延迟报文干扰后续复用相同四元组的新连接。
如果 TIME_WAIT 期间收到有效的重传 FIN:
客户端 服务端
| |
|<------------ FIN --------------| LAST_ACK
| |
|------------- ACK --X | ACK 丢失
| |
TIME_WAIT |
| |
|<------------ FIN --------------| 重传 FIN
| |
|------------- ACK ------------->|
| | CLOSED
TIME_WAIT
|
重新计时 2MSL
|
CLOSED注意:TIME_WAIT 是状态,2MSL 是等待时长。收到有效的重传 FIN 后,客户端仍处于 TIME_WAIT,并重新计时。
五、TCP 关闭状态总结
六、面试重点总结
1. 为什么三次握手、四次挥手?
建立连接时 SYN 和 ACK 可以合并发送;关闭连接时被动方可能还有数据需要发送,ACK 和 FIN 通常分开发送。
2. CLOSE_WAIT 是什么?
被动关闭方收到 FIN 并回复 ACK 后,等待应用关闭连接。
3. LAST_ACK 是什么?
被动关闭方发送 FIN 后,等待最后一个 ACK。
4. TIME_WAIT 为什么等待 2MSL?
为了处理最后 ACK 丢失导致的 FIN 重传,以及减少旧连接延迟报文对新连接的影响。
5. CLOSE_WAIT 大量堆积如何处理?
先使用 ss 定位进程和端口,再检查线程堆栈、Netty Channel 生命周期与应用连接关闭逻辑。
核心记忆:
FIN_WAIT_1:主动方发送 FIN,等确认。FIN_WAIT_2:主动方等对方 FIN。CLOSE_WAIT:被动方等自己应用关闭。LAST_ACK:被动方等最后 ACK。
