基于本文回答
0
评论

TCP四次挥手机制

知识点图片

本文讲解TCP四次挥手:一个为保证数据完整性的“礼貌分手”过程。因TCP全双工特性,连接关闭需双向确认,一方的确认(ACK)和关闭请求(FIN)分开发送,故需四次。客户端最后的TIME_WAIT状态确保了连接的可靠关闭

我们来详细讲解一下TCP的四次挥手(Four-way Handshake)原理。这是断开TCP连接的过程。

一、一句话比喻:礼貌地分手

如果说三次握手是“打电话确认”,那么四次挥手就像是一场礼貌而严谨的分手过程

  1. 第一次挥手(你提出分手)

    • :“我想我们说完了,我这边没什么要说的了。”
    • 你的目的:告诉对方,你不会再发送新的数据了。
  2. 第二次挥手(对方确认收到)

    • 对方:“好的,我知道你没话说了。但请等一下,我这边还有几句话没说完。”
    • 对方的目的:1. 确认收到了你的“分手”请求。 2. 告诉你他可能还有数据要发送,让你稍等。
  3. 第三次挥手(对方也说完了)

    • 对方:“好了,现在我这边也说完了,我们正式结束吧。”
    • 对方的目的:告诉对方,他也准备好关闭连接了。
  4. 第四次挥手(你最后确认)

    • :“好的,我收到了你的结束请求。再见。”
    • 你的目的:确认收到了对方的关闭请求,让对方可以安心地关闭。自己则再等一小会儿,确保万无一失后也关闭。

这个过程确保了双方都有机会把话说完,不会因为一方突然挂断而导致信息丢失。


二、四次挥手的核心目的

TCP是全双工的,意味着数据可以在两个方向上同时流动。因此,关闭连接也必须是双向的,每一方都必须独立地关闭自己的发送通道。四次挥手的核心目的就是安全、优雅地断开连接(Graceful Shutdown),确保:

  1. 数据完整性:保证双方在连接断开前,所有排队的数据都已发送和接收完毕。
  2. 双向关闭:每一方都能明确地知道对方已经同意关闭连接。

三、技术细节:四次挥手的详细过程

在TCP头部,我们这次主要关注两个标志位:

  • FIN (Finish):结束标志位。当FIN=1时,表示发送方的数据已经发送完毕,请求关闭连接。
  • ACK (Acknowledge):确认标志位。用于响应收到的报文。

下面是四次挥手的具体步骤(假设客户端主动发起关闭):

第一次挥手(Client -> Server)

  • 状态:客户端和服务器都处于 ESTABLISHED 状态。
  • 客户端操作
    1. 客户端应用程序调用 close() 函数,触发TCP层发送一个关闭请求。
    2. 客户端发送一个FIN报文,其中标志位 FIN 设置为 1,并包含一个序列号 seq = u(u是客户端这边最后发送的数据的序列号+1)。
  • 报文内容FIN = 1, seq = u
  • 客户端状态变化:发送后,客户端进入 FIN_WAIT_1 状态。此时,客户端不能再发送数据,但仍然可以接收数据。

通俗解释:客户端对服务器说:“我这边的数据都发完了,我要关闭发送通道了。”

第二次挥手(Server -> Client)

  • 服务器操作
    1. 服务器收到FIN报文后,知道客户端想关闭连接。
    2. 服务器发送一个ACK报文作为回应。确认号设置为 ack = u + 1
  • 报文内容ACK = 1, seq = v(v是服务器这边最后发送的数据的序列号+1), ack = u + 1
  • 双方状态变化
    • 服务器发送ACK后,进入 CLOSE_WAIT 状态。此时,服务器到客户端的连接是半关闭的。服务器还能向客户端发送数据,但客户端不能再向服务器发送新数据。
    • 客户端收到这个ACK后,进入 FIN_WAIT_2 状态,等待服务器发送它自己的FIN报文。

通俗解释:服务器对客户端说:“收到你的关闭请求了。但我可能还有数据没发完,你等我一下。”

第三次挥手(Server -> Client)

  • 服务器操作
    1. 当服务器也完成了所有数据的发送后,其应用程序也会调用 close() 函数。
    2. 服务器发送一个FIN报文给客户端,标志位 FIN 设置为 1,并包含自己的序列号 seq = w(w是服务器这边最后发送的数据的序列号+1)。
  • 报文内容FIN = 1, ACK = 1, seq = w, ack = u + 1(这个ACK是用来应对可能重传的报文)
  • 服务器状态变化:发送后,服务器进入 LAST_ACK 状态,等待客户端的最后确认。

通俗解释:服务器对客户端说:“我这边的数据也发完了,现在我也要关闭发送通道了。”

第四次挥手(Client -> Server)

  • 客户端操作
    1. 客户端收到服务器的FIN报文后,知道服务器也准备好关闭了。
    2. 客户端发送最后一个ACK报文作为确认。确认号设置为 ack = w + 1
  • 报文内容ACK = 1, seq = u + 1, ack = w + 1
  • 双方状态变化
    • 客户端发送最后一个ACK后,进入 TIME_WAIT 状态。这个状态会持续 2 * MSL (Maximum Segment Lifetime,最大报文生存时间) 的时长。之后,客户端才会进入 CLOSED 状态。
    • 服务器收到这个ACK后,立即进入 CLOSED 状态,连接正式关闭。

通俗解释:客户端对服务器说:“好的,我收到了你的关闭请求。再见!” 然后客户端会默默等待一段时间再彻底离开。


四、经典问题解答

1. 为什么是四次,而不是三次?

核心原因:TCP是全双工的,服务器在收到客户端的关闭请求(FIN)时,可能自己还有数据没有发送完毕。

  • 在第二次挥手中,服务器发送的 ACK 仅仅是对客户端 FIN 的确认,表示“我知道你要关闭了”。
  • 但此时服务器可能还在发送数据,所以不能立即发送自己的 FIN
  • 只有当服务器的所有数据都发送完毕后,它才能发送 FIN(第三次挥手),表示“我也准备好关闭了”。
  • 因此,第二次的 ACK 和第三次的 FIN 通常是分开发送的,这就导致了总共需要四次挥手。
  • (特殊情况:如果服务器在收到客户端FIN时,恰好也没有数据要发送,那么它可以将第二次的ACK和第三次的FIN合并在一个报文中发送,此时就变成了“三次挥手”。但这只是巧合,标准流程是四次。)

2. 为什么客户端最后要进入 TIME_WAIT 状态并等待 2*MSL?

这是一个非常重要的机制,主要有两个目的:

  1. 确保第四次挥手的ACK报文能够到达服务器。

    • 如果客户端发送的最后一个ACK在网络中丢失了,那么服务器在 LAST_ACK 状态下会收不到确认。
    • 服务器会超时重传它的 FIN 报文。
    • 如果客户端此时已经 CLOSED 了,它将无法响应这个重传的 FIN。但如果客户端处于 TIME_WAIT 状态,它就能重新发送一次 ACK,帮助服务器正常关闭。
    • 2*MSL 的时间足以保证:一个 FIN 的传输(1 MSL) + 一个 ACK 的返回(1 MSL)。
  2. 防止“已失效的连接请求报文段”出现在本连接中。

    • TIME_WAIT状态持续的时间内,可以确保本次连接中所有在网络中滞留的、延迟的报文段都已消失。
    • 这样,当一个新的连接(可能使用相同的IP和端口号)建立时,就不会收到上一次连接残留的旧数据包,从而避免了数据混淆。

总结

TCP四次挥手是一个确保数据完整和连接可靠关闭的严谨过程。它通过四次通信,让通信双方都能优雅地结束自己的发送任务,并最终安全地释放连接资源。CLOSE_WAIT 状态解释了为什么需要四次挥手,而 TIME_WAIT 状态则体现了TCP协议在可靠性方面的极致追求。

右滑查看面试常问