TCP 为什么要四次挥手?能不能三次?
简化版
四次挥手是断开 TCP 连接的过程。因为 TCP 是全双工(两个方向独立),关闭要两个方向各关一次:一方发 FIN 说「我没数据要发了」,另一方 ACK 确认;反过来另一方也发 FIN、对方再 ACK,共四次。之所以是四次而不是三次,是因为被动方收到 FIN 后,可能还有数据没发完,所以它的 ACK 和 FIN 通常不能合并,要分两次发。
详细版
四次挥手过程(假设客户端主动关闭):
- 客户端 → FIN:客户端发
FIN,表示「我不再发送数据了」,进入FIN_WAIT_1。 - 服务端 → ACK:服务端确认,进入
CLOSE_WAIT;客户端收到后进入FIN_WAIT_2。此时服务端到客户端方向还能发数据(半关闭状态)。 - 服务端 → FIN:服务端把剩余数据发完后,也发
FIN,进入LAST_ACK。 - 客户端 → ACK:客户端确认,进入
TIME_WAIT;服务端收到后进入CLOSED。客户端等待 2MSL 后也进入CLOSED。
为什么是四次不是三次:三次握手时,服务端的 SYN 和 ACK 能合并成一个包(SYN+ACK),因为建连时它没有别的数据要处理。但挥手时,服务端收到客户端的 FIN 后,它可能还有数据要发给客户端,所以只能先单独回 ACK(我知道你要关了),等自己数据发完再发 FIN。ACK 和 FIN 分开,就成了四次。
完整版教学
一、根源:TCP 全双工,关闭要「各关各的」
TCP 连接是全双工的——A→B 和 B→A 是两条独立的数据通道。所以「关闭连接」本质上是关闭两个方向,每个方向都要「一方声明关闭(FIN)+ 另一方确认(ACK)」。两个方向 × 两步 = 四次。
对比三次握手:建连时要同步双方的初始序列号,双方各需一次 SYN + 一次 ACK,也是四个动作,但服务端的 ACK 和 SYN 能合并成一个包,所以三次。挥手时服务端的 ACK 和 FIN 常常不能合并,所以四次。区别就在于「能不能把 ACK 和 FIN/SYN 合并」。
二、半关闭:为什么 ACK 和 FIN 要分开
第 2、3 步之间的状态叫半关闭(half-close):客户端已经不再发数据(它的 FIN 发过了),但服务端仍可以继续向客户端发送数据。
这正是四次的关键——服务端收到客户端 FIN 时,可能手头还有数据没发完。它必须:
- 先回 ACK:告诉客户端「你的关闭请求我收到了」,别让客户端超时重发 FIN;
- 继续把剩余数据发完;
- 再发 FIN:等自己也没数据了,才声明关闭。
如果服务端没有剩余数据,理论上 ACK 和 FIN 可以合并成三次(这也是一种优化,实际中「延迟确认」有时会让它们合并)。但一般情况下要预留「发完剩余数据」的余地,所以标准是四次。
三、TIME_WAIT:主动关闭方为什么要等 2MSL
主动关闭方(这里是客户端)发出最后一个 ACK 后,不会立即 CLOSED,而是进入 TIME_WAIT,等待 2MSL(MSL = 报文最大生存时间)才真正关闭。原因有两个:
- 保证最后的 ACK 能到达对方:万一这个 ACK 丢了,服务端会超时重发 FIN,客户端还在 TIME_WAIT 就能重新回 ACK。如果客户端直接关了,重发的 FIN 就没人应答,服务端一直卡在 LAST_ACK。
- 让本次连接的旧数据包在网络中彻底消失:等 2MSL(一来一回的最大时间)后,旧连接的残留报文都过期了,避免它们「串扰」到后续用相同端口的新连接。
TIME_WAIT 和 CLOSE_WAIT 是两个不同角色的状态,也是高频考点,详见「TIME_WAIT 和 CLOSE_WAIT 的区别」那道题。
四、异常情况:如果不按规矩挥手
- 服务端不发 FIN(一直不关):客户端会长期停在
FIN_WAIT_2。系统一般有超时机制回收。 - 大量 CLOSE_WAIT 堆积:说明服务端收到了 FIN 却迟迟不关闭连接——通常是代码 bug(没有正确
close()socket)。这是排查连接泄漏的重要信号。 - 直接 RST:如果一方想强制中断(如进程崩溃、
SO_LINGER设 0),会发RST直接重置连接,跳过四次挥手,另一方会收到「connection reset」。
五、常见误区
- ❌ 以为挥手一定是四次、握手一定是三次是「规定」——本质都是四个动作,区别在能否合并 ACK。
- ❌ 以为发完 FIN 就不能收数据——半关闭下,主动方发完 FIN 仍能接收对方数据。
- ❌ 把 TIME_WAIT 当成有害要消灭——它是保证可靠关闭的必要机制,大量堆积才需调优。
- ❌ 大量 CLOSE_WAIT 以为是对端问题——CLOSE_WAIT 在自己这侧,通常是自己没 close。
六、常见误区与追问
| 考点 | 正确口径 |
|---|---|
| 第一次 | 主动方发送 FIN,表示自己不再发送数据 |
| 第二次 | 被动方 ACK,表示收到关闭请求 |
| 第三次 | 被动方发送 FIN,表示自己也发完了 |
| 第四次 | 主动方 ACK,并进入 TIME_WAIT |
A -> FIN -> B
A <- ACK <- B
A <- FIN <- B
A -> ACK -> B
A enters TIME_WAIT
四次挥手的根源是 TCP 全双工:两个方向的数据通道要分别关闭。
- 误区:TCP 关闭必须总是四个独立报文。 被动方 ACK 和 FIN 有时可合并,但逻辑上仍是两个方向分别关闭。
- 误区:收到 FIN 就不能再收数据。 收到对方 FIN 只表示对方不再发送,自己仍可继续发送剩余数据。
- 误区:TIME_WAIT 是服务器特有状态。 TIME_WAIT 在主动关闭方,客户端或服务器都可能进入。
- 追问:为什么不能三次挥手? 被动方收到 FIN 后可能还有数据没发完,ACK 和自己的 FIN 不能总是立即合并。
- 追问:TIME_WAIT 为什么要等 2MSL? 确保最后 ACK 可重传,并让旧连接报文在网络中自然消失。
- 追问:CLOSE_WAIT 多说明什么? 通常说明应用收到关闭后没有及时 close,属于应用层资源释放问题。
七、加强记忆
TCP 全双工,关闭要关两个方向,每个方向「FIN + ACK」,共四次。四次而非三次,是因为被动方收到 FIN 后可能还有数据要发,只能先单独回 ACK、数据发完再发 FIN,两者无法合并。主动关闭方最后等 2MSL(保证末尾 ACK 送达 + 让旧报文消散)才真正关闭。