[面试题] TCP三次握手、四次挥手机制是什么?为什么不是四次握手?为什么不是三次挥手?
📖 版块 A:知识讲解版 (温故知新)
-
三次握手 (Three-way Handshake):
- 第一次握手:客户端发送 SYN 包(seq=x)到服务器,进入 SYN_SEND 状态。
- 第二次握手:服务器收到 SYN 包,发送 SYN+ACK 包(seq=y, ack=x+1)给客户端,进入 SYN_RECV 状态。
- 第三次握手:客户端收到 SYN+ACK 包,发送 ACK 包(ack=y+1)给服务器,双方进入 ESTABLISHED 状态。
- 核心目的:同步序列号(ISN),确认双方的接收与发送能力均正常。
-
四次挥手 (Four-way Teardown):
- 第一次挥手:客户端发送 FIN 包,进入 FIN_WAIT_1 状态。
- 第二次挥手:服务端收到 FIN,发送 ACK,进入 CLOSE_WAIT 状态;客户端收到后进入 FIN_WAIT_2。
- 第三次挥手:服务端处理完剩余数据,发送 FIN,进入 LAST_ACK 状态。
- 第四次挥手:客户端收到 FIN,发送 ACK,进入 TIME_WAIT 状态,等待 2MSL 后关闭;服务端收到 ACK 后进入 CLOSED 状态。
-
关键考点:
- 为什么不是二次:无法防止已失效的连接请求报文突然又传送到了服务端,导致资源浪费。
- 为什么不是四次(握手):服务端将 SYN 和 ACK 合并发送(捎带应答),效率更高,四次则显得冗余。
- 为什么挥手多一次:TCP 是全双工的,一端停止发送(FIN)并不代表另一端也停止发送,中间的 CLOSE_WAIT 阶段允许服务端处理完剩余业务数据。
🎤 版块 B:面试实战版 (高手逐字稿)
Q:TCP三次握手、四次挥手机制是什么?为什么不是四次握手?为什么不是三次挥手?
💬 高手逐字稿:
关于 TCP 的三次握手和四次挥手,我们可以从底层逻辑来拆解它的必要性。
首先,三次握手本质上是客户端和服务端为了确认两件事:第一是确认彼此的接收和发送能力是否正常,第二是同步初始序列号(ISN)。
- 第一次握手,服务端确认了“对方发包正常,我接包正常”;
- 第二次握手,客户端确认了“我发包、接包都正常,对方发包、接包也正常”;
- 第三次握手,服务端确认了“对方接包正常,我发包正常”。至此,闭环完成。
为什么不是两次握手? 主要是为了防止“已失效的连接请求”造成脏连接。比如客户端发出的第一个 SYN 在网络中由于阻塞滞留了,客户端超时重传了第二个 SYN 并完成了连接。等第一个 SYN 延迟到达服务端时,如果是两次握手,服务端只要一响应,连接就建立了,但这其实是一个无效连接,会白白浪费服务器资源。
为什么不是四次握手? 其实在逻辑上,服务端回复 ACK 和发送自己的 SYN 是可以分两次发的,但为了性能优化,TCP 协议将这两个动作合并为一个包(捎带应答),所以三次就足够了,四次没有必要。
接着看四次挥手。这是由于 TCP **全双工(Full Duplex)**的特性决定的。当客户端发出 FIN 报文时,只代表客户端不再发送数据了,但它仍然具备接收数据的能力。此时服务端可能还有未处理完的数据要发给客户端,所以服务端会先回一个 ACK 告知“我知道你要断开了”,但并不立刻发 FIN,而是进入 CLOSE_WAIT 状态处理剩余业务。等服务端处理完毕,才会发出自己的 FIN。正是因为服务端的 ACK 和 FIN 通常不能同时发送,才导致了比握手多出了一次交互。
这里有一个容易被忽略的细节,就是客户端在第四次挥手后,为什么要进入 TIME_WAIT 状态并等待 2MSL(两倍最大报文段寿命)时间?我认为主要有两个原因:第一,为了保证最后一次 ACK 报文能够到达服务端。如果这个 ACK 丢了,服务端会重发 FIN,客户端在 2MSL 时间内收到重发的 FIN 后,可以再次补发 ACK。第二,防止“已失效的连接请求报文”出现在下一个新的连接中。2MSL 时间足以让本次连接产生的所有报文在网络中自然消失,避免旧连接的数据干扰新连接。
如果在生产环境中观察到大量的 TIME_WAIT,通常说明服务器存在大量短连接;而如果观察到大量的 CLOSE_WAIT,往往意味着应用层代码没有正常调用 close() 方法释放资源。