TCP四次挥手机制
本文讲解TCP四次挥手:一个为保证数据完整性的“礼貌分手”过程。因TCP全双工特性,连接关闭需双向确认,一方的确认(ACK)和关闭请求(FIN)分开发送,故需四次。客户端最后的TIME_WAIT状态确保了连接的可靠关闭
我们来详细讲解一下TCP的四次挥手(Four-way Handshake)原理。这是断开TCP连接的过程。
一、一句话比喻:礼貌地分手
如果说三次握手是“打电话确认”,那么四次挥手就像是一场礼貌而严谨的分手过程。
第一次挥手(你提出分手):
- 你:“我想我们说完了,我这边没什么要说的了。”
- 你的目的:告诉对方,你不会再发送新的数据了。
第二次挥手(对方确认收到):
- 对方:“好的,我知道你没话说了。但请等一下,我这边还有几句话没说完。”
- 对方的目的:1. 确认收到了你的“分手”请求。 2. 告诉你他可能还有数据要发送,让你稍等。
第三次挥手(对方也说完了):
- 对方:“好了,现在我这边也说完了,我们正式结束吧。”
- 对方的目的:告诉对方,他也准备好关闭连接了。
第四次挥手(你最后确认):
- 你:“好的,我收到了你的结束请求。再见。”
- 你的目的:确认收到了对方的关闭请求,让对方可以安心地关闭。自己则再等一小会儿,确保万无一失后也关闭。
这个过程确保了双方都有机会把话说完,不会因为一方突然挂断而导致信息丢失。
二、四次挥手的核心目的
TCP是全双工的,意味着数据可以在两个方向上同时流动。因此,关闭连接也必须是双向的,每一方都必须独立地关闭自己的发送通道。四次挥手的核心目的就是安全、优雅地断开连接(Graceful Shutdown),确保:
- 数据完整性:保证双方在连接断开前,所有排队的数据都已发送和接收完毕。
- 双向关闭:每一方都能明确地知道对方已经同意关闭连接。
三、技术细节:四次挥手的详细过程
在TCP头部,我们这次主要关注两个标志位:
- FIN (Finish):结束标志位。当
FIN=1时,表示发送方的数据已经发送完毕,请求关闭连接。 - ACK (Acknowledge):确认标志位。用于响应收到的报文。
下面是四次挥手的具体步骤(假设客户端主动发起关闭):
第一次挥手(Client -> Server)
- 状态:客户端和服务器都处于
ESTABLISHED状态。 - 客户端操作:
- 客户端应用程序调用
close()函数,触发TCP层发送一个关闭请求。 - 客户端发送一个FIN报文,其中标志位
FIN设置为1,并包含一个序列号seq = u(u是客户端这边最后发送的数据的序列号+1)。
- 客户端应用程序调用
- 报文内容:
FIN = 1,seq = u - 客户端状态变化:发送后,客户端进入
FIN_WAIT_1状态。此时,客户端不能再发送数据,但仍然可以接收数据。
通俗解释:客户端对服务器说:“我这边的数据都发完了,我要关闭发送通道了。”
第二次挥手(Server -> Client)
- 服务器操作:
- 服务器收到FIN报文后,知道客户端想关闭连接。
- 服务器发送一个ACK报文作为回应。确认号设置为
ack = u + 1。
- 报文内容:
ACK = 1,seq = v(v是服务器这边最后发送的数据的序列号+1),ack = u + 1 - 双方状态变化:
- 服务器发送ACK后,进入
CLOSE_WAIT状态。此时,服务器到客户端的连接是半关闭的。服务器还能向客户端发送数据,但客户端不能再向服务器发送新数据。 - 客户端收到这个ACK后,进入
FIN_WAIT_2状态,等待服务器发送它自己的FIN报文。
- 服务器发送ACK后,进入
通俗解释:服务器对客户端说:“收到你的关闭请求了。但我可能还有数据没发完,你等我一下。”
第三次挥手(Server -> Client)
- 服务器操作:
- 当服务器也完成了所有数据的发送后,其应用程序也会调用
close()函数。 - 服务器发送一个FIN报文给客户端,标志位
FIN设置为1,并包含自己的序列号seq = w(w是服务器这边最后发送的数据的序列号+1)。
- 当服务器也完成了所有数据的发送后,其应用程序也会调用
- 报文内容:
FIN = 1,ACK = 1,seq = w,ack = u + 1(这个ACK是用来应对可能重传的报文) - 服务器状态变化:发送后,服务器进入
LAST_ACK状态,等待客户端的最后确认。
通俗解释:服务器对客户端说:“我这边的数据也发完了,现在我也要关闭发送通道了。”
第四次挥手(Client -> Server)
- 客户端操作:
- 客户端收到服务器的FIN报文后,知道服务器也准备好关闭了。
- 客户端发送最后一个ACK报文作为确认。确认号设置为
ack = w + 1。
- 报文内容:
ACK = 1,seq = u + 1,ack = w + 1 - 双方状态变化:
- 客户端发送最后一个ACK后,进入
TIME_WAIT状态。这个状态会持续2 * MSL(Maximum Segment Lifetime,最大报文生存时间) 的时长。之后,客户端才会进入CLOSED状态。 - 服务器收到这个ACK后,立即进入
CLOSED状态,连接正式关闭。
- 客户端发送最后一个ACK后,进入
通俗解释:客户端对服务器说:“好的,我收到了你的关闭请求。再见!” 然后客户端会默默等待一段时间再彻底离开。
四、经典问题解答
1. 为什么是四次,而不是三次?
核心原因:TCP是全双工的,服务器在收到客户端的关闭请求(FIN)时,可能自己还有数据没有发送完毕。
- 在第二次挥手中,服务器发送的
ACK仅仅是对客户端FIN的确认,表示“我知道你要关闭了”。 - 但此时服务器可能还在发送数据,所以不能立即发送自己的
FIN。 - 只有当服务器的所有数据都发送完毕后,它才能发送
FIN(第三次挥手),表示“我也准备好关闭了”。 - 因此,第二次的
ACK和第三次的FIN通常是分开发送的,这就导致了总共需要四次挥手。 - (特殊情况:如果服务器在收到客户端FIN时,恰好也没有数据要发送,那么它可以将第二次的ACK和第三次的FIN合并在一个报文中发送,此时就变成了“三次挥手”。但这只是巧合,标准流程是四次。)
2. 为什么客户端最后要进入 TIME_WAIT 状态并等待 2*MSL?
这是一个非常重要的机制,主要有两个目的:
确保第四次挥手的ACK报文能够到达服务器。
- 如果客户端发送的最后一个ACK在网络中丢失了,那么服务器在
LAST_ACK状态下会收不到确认。 - 服务器会超时重传它的
FIN报文。 - 如果客户端此时已经
CLOSED了,它将无法响应这个重传的FIN。但如果客户端处于TIME_WAIT状态,它就能重新发送一次ACK,帮助服务器正常关闭。 2*MSL的时间足以保证:一个FIN的传输(1 MSL) + 一个ACK的返回(1 MSL)。
- 如果客户端发送的最后一个ACK在网络中丢失了,那么服务器在
防止“已失效的连接请求报文段”出现在本连接中。
- 在
TIME_WAIT状态持续的时间内,可以确保本次连接中所有在网络中滞留的、延迟的报文段都已消失。 - 这样,当一个新的连接(可能使用相同的IP和端口号)建立时,就不会收到上一次连接残留的旧数据包,从而避免了数据混淆。
- 在
总结
TCP四次挥手是一个确保数据完整和连接可靠关闭的严谨过程。它通过四次通信,让通信双方都能优雅地结束自己的发送任务,并最终安全地释放连接资源。CLOSE_WAIT 状态解释了为什么需要四次挥手,而 TIME_WAIT 状态则体现了TCP协议在可靠性方面的极致追求。