写点什么

TCP 连接断开:为什么要挥手四次

  • 2023-12-13
    广东
  • 本文字数:3485 字

    阅读完需:约 11 分钟

TCP连接断开:为什么要挥手四次

本文分享自华为云社区《解密TCP连接断开:四次挥手的奥秘和数据传输的安全》,作者: 努力的小雨 。

TCP 连接断开


在当今数字化时代,互联网已经成为了人们生活中不可或缺的一部分。而在互联网的基础之上,TCP 协议扮演着关键的角色,它负责着数据在网络中的可靠传输。在 TCP 连接的建立过程中,我们已经了解了三次握手的过程和原理。然而,连接的建立只是 TCP 协议的一部分,同样重要的是连接的断开过程。本文将重点探讨 TCP 连接的断开过程,包括四次挥手的过程和状态变迁,以及为什么挥手需要四次和为什么需要 TIME_WAIT 状态。通过深入理解 TCP 连接断开的过程,我们可以更好地理解网络通信的原理。

TCP 四次挥手过程和状态变迁


TCP 断开连接需要通过四次挥手的方式。双方都有能力主动断开连接,一旦断开连接,主机中的各种「资源」将被释放。那么我们将详细讲解下 TCP 四次挥手的原理及过程!



  • 当客户端打算关闭连接时,它会发送一个 TCP 首部中 FIN 标志位被置为 1 的报文,即 FIN 报文。随后,客户端进入 FIN_WAIT_1 状态。

  • 当服务端收到该报文后,会向客户端发送一个 ACK 应答报文,并进入 CLOSED_WAIT 状态。

  • 客户端接收到服务端的 ACK 应答报文后,进入 FIN_WAIT_2 状态。

  • 服务端等待处理完数据后,也会向客户端发送一个 FIN 报文,然后进入 LAST_ACK 状态。

  • 客户端收到服务端的 FIN 报文后,会回复一个 ACK 应答报文,并进入 TIME_WAIT 状态。

  • 一旦服务端收到了 ACK 应答报文,就进入 CLOSE 状态,这样服务端就完成了连接的关闭。

  • 客户端经过 2MSL 一段时间后,自动进入 CLOSE 状态,这样客户端也完成了连接的关闭。


在 TCP 连接的断开过程中,我们可以观察到每个方向都需要发送一个 FIN 报文和接收一个 ACK 报文,因此通常将这个过程称为四次挥手。


需要注意的一点是,只有主动发起关闭连接的一方,才会进入 TIME_WAIT 状态。这是因为在关闭连接后,客户端需要等待一段时间(通常为两倍的最大报文段生存时间,也即 2MSL)来确保服务端收到了自己的 ACK 应答报文。这样做的目的是为了防止已经关闭的连接上出现延迟的报文段,确保连接的可靠关闭。而服务端则不需要等待这段时间,因此没有 TIME_WAIT 状态。

为什么挥手需要四次?


为了更好地理解为什么挥手需要四次,让我们再来回顾一下双方发出 FIN 包的过程。这样我们就能理解为什么需要四次挥手了。


在关闭连接时,当客户端向服务端发送 FIN 时,这仅仅表示客户端不再发送数据了,但是它仍然可以接收数据。


当服务端收到客户端的 FIN 报文时,它首先会回复一个 ACK 应答报文。然而,服务端可能还有数据需要处理和发送,所以它会等待直到它不再发送数据时,才会发送 FIN 报文给客户端,表示同意现在关闭连接。


通过上述过程,我们可以看出,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK 和 FIN 通常会分开发送,这就导致了比三次握手多了一次挥手的过程。

为什么 TIME_WAIT 等待的时间是 2MSL?


MSL 是 Maximum Segment Lifetime,即报文的最大生存时间,它表示报文在网络中存在的最长时间。超过此时间,报文将被丢弃。因为 TCP 协议是基于 IP 协议的,IP 头部有一个 TTL 字段,它表示数据报可以经过的最大路由数。每经过一个路由器,TTL 值就减 1。当 TTL 值为 0 时,数据报将被丢弃,并且发送 ICMP 报文通知源主机。


MSL 和 TTL 的区别在于单位。MSL 的单位是时间,而 TTL 是经过的路由跳数。因此,为了确保报文已经自然消亡,MSL 应该大于或等于 TTL 消耗为 0 的时间。


TIME_WAIT 等待 2 倍 MSL 的合理解释是:网络中可能存在来自发送方的数据包。当这些数据包被接收方处理后,它会向对方发送响应,因此往返需要等待 2 倍的时间。就是确保最后一个 ACK 被服务端接收到了,如果没有接收到也要给足时间让服务器端的第三次挥手的 FIN 重新传过来。


举个例子,如果被动关闭方没有收到断开连接的最后一个 ACK 报文,就会触发超时重发 FIN 报文。另一方收到 FIN 报文后,会重发 ACK 给被动关闭方,这样来回就需要 2 个 MSL 的时间。


2MSL 时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME_WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。


在 Linux 系统中,默认的 2MSL 时间是 60 秒,即一个 MSL 为 30 秒。Linux 系统停留在 TIME_WAIT 状态的时间是固定的 60 秒。在 Linux 内核代码中,它的定义名为 TCP_TIMEWAIT_LEN:


#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT                                     state, about 60 seconds  */
复制代码


如果要修改 TIME_WAIT 的时间长度,只能修改 Linux 内核代码中 TCP_TIMEWAIT_LEN 的值,并重新编译 Linux 内核。

为什么需要 TIME_WAIT 状态?


TIME_WAIT 状态的存在是为了确保网络连接的可靠关闭。只有主动发起关闭连接的一方(即主动关闭方)才会有 TIME_WAIT 状态。


TIME_WAIT 状态的需求主要有两个原因:


  • 防止具有相同「四元组」的「旧」数据包被收到:在网络通信中,每个 TCP 连接都由源 IP 地址、源端口号、目标 IP 地址和目标端口号这四个元素唯一标识,称为「四元组」。当一方主动关闭连接后,进入 TIME_WAIT 状态,它仍然可以接收到一段时间内来自对方的延迟数据包。这是因为网络中可能存在被延迟传输的数据包,如果没有 TIME_WAIT 状态的存在,这些延迟数据包可能会被错误地传递给新的连接,导致数据混乱。通过保持 TIME_WAIT 状态,可以防止旧的数据包干扰新的连接。

  • 保证「被动关闭连接」的一方能被正确关闭:当连接的被动关闭方接收到主动关闭方的 FIN 报文(表示关闭连接),它需要发送一个确认 ACK 报文给主动关闭方,以完成连接的关闭。然而,网络是不可靠的,ACK 报文可能会在传输过程中丢失。如果主动关闭方在收到 ACK 报文之前就关闭连接,被动关闭方将无法正常完成连接的关闭。TIME_WAIT 状态的存在确保了被动关闭方能够接收到最后的 ACK 报文,从而帮助其正常关闭连接。

防止旧连接的数据包


假设 TIME-WAIT 状态没有适当的等待时间或时间过短,延迟的数据包抵达后可能会引发严重的问题。



例如,服务端在关闭连接之前发送的 SEQ = 301 报文被网络延迟了。然后,同一端口的 TCP 连接被复用,并且延迟的 SEQ = 301 到达了客户端。在这种情况下,客户端有可能正常地接收到这个过期的报文,从而导致数据错乱等严重问题的发生。


为了解决这个问题,TCP 设计了一个机制,即经过 2MSL 的时间,足够让连接中的两个方向上的数据包都被丢弃。这样,原来连接的数据包在网络中自然消失,再出现的数据包一定是由新建立的连接产生的,从而避免了数据错乱等问题的发生。

保证连接正确关闭


TIME-WAIT 状态的作用是等待足够的时间,以确保最后的 ACK 报文能够被被动关闭方接收,并帮助其正常关闭。


假设 TIME-WAIT 没有适当的等待时间或时间过短,断开连接可能会导致以下问题:



例如,如果在四次挥手的过程中,客户端发送的最后一个 ACK 报文在网络中丢失,并且客户端的 TIME-WAIT 状态过短或没有设置,则客户端会直接进入 CLOSE 状态,而服务端则会一直处于 LAST-ACK 状态。这种情况下,连接无法正常关闭。


另外,当客户端发起建立连接的 SYN 请求后,如果服务端发送的 RST 报文给客户端,连接建立的过程将会被终止。


如果 TIME-WAIT 等待的时间足够长,会发生以下两种情况:


  • 服务端正常接收到四次挥手的最后一个 ACK 报文,从而正常关闭连接。

  • 服务端没有收到四次挥手的最后一个 ACK 报文时,会重发 FIN 关闭连接报文并等待新的 ACK 报文。


因此,客户端在 TIME-WAIT 状态等待 2MSL 时间后,可以确保双方的连接都能够正常关闭。


这里再科普一下有关知识,大多数三次握手和四次挥手都没有提到。为什么第三次挥手的时候会发送 ack 呢?不是正常就是发送 fin 就可以了吗?


在 TCP 协议中,除了初始连接的第一个 SYN 包,其中 ACK 字段被设置为 0,而其他所有的 TCP 包都会将 ACK 字段设置为 1。这个 ACK 字段的作用是用来确认接收方已经成功接收到数据。如果有数据需要发送,TCP 协议会在发送数据的同时附带 ACK 来确认对方的数据。如果数据在传输过程中丢失,TCP 会进行数据重传。


ACK 字段是 TCP 头部必备的,这 32 个位空着也是空着,那么干脆让除了初始报文段之外的所有报文段的 ACK 字段都有效。

总结


TCP 连接的断开需要通过四次挥手的过程来完成。双方都有能力主动断开连接,并且在断开连接后,各种资源将被释放。四次挥手的过程涉及到双方发送 FIN 和 ACK 报文的交互,确保数据的可靠传输和连接的正确关闭。其中,主动关闭方会进入 TIME_WAIT 状态,等待一段时间来确保对方已经接收到最后的 ACK 报文。


TIME_WAIT 状态的存在是为了防止旧连接的数据包干扰新连接,并确保被动关闭方能够正常关闭连接。挥手需要四次的原因是为了确保数据的完整传输和连接的可靠关闭。TIME_WAIT 状态等待 2 倍 MSL 的时间是为了确保网络中的数据包都已经消失。


点击关注,第一时间了解华为云新鲜技术~

发布于: 2023-12-13阅读数: 2
用户头像

提供全面深入的云计算技术干货 2020-07-14 加入

生于云,长于云,让开发者成为决定性力量

评论

发布
暂无评论
TCP连接断开:为什么要挥手四次_开发_华为云开发者联盟_InfoQ写作社区