TCP四次挥手详解[通俗易懂]

TCP四次挥手详解[通俗易懂]在开始之前可以先了解一下TCP三次握手TCP四次挥手过程和状态变迁在断开连接之前客户端和服务器都处于ESTABLISHED状态,双方都可以主动断开连接,以客户端主动断开连接为优。第一次挥手:客户端打算断开连接,向服务器发送FIN报文(FIN标记位被设置为1,1表示为FIN,0表示不是),FIN报文中会指定一个序列号,之后客户端进入FIN_WAIT_1状态。也就是客户端发出连接释放报文段(FIN报文),指定序列号seq=u,主动关闭TCP连接,等待服务器的确认。第二次挥…

大家好,又见面了,我是你们的朋友全栈君。


在开始之前可以先了解一下     TCP三次握手


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

为什么挥手需要四次?

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

等待2MSL的意义

TIME_WAIT状态过多有什么危害?

如何解决TIME_WAIT状态过多?

TCP四次挥手详解[通俗易懂]


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

 TCP四次挥手详解[通俗易懂]

在断开连接之前客户端和服务器都处于ESTABLISHED状态,双方都可以主动断开连接,以客户端主动断开连接为优。

第一次挥手:客户端打算断开连接,向服务器发送FIN报文(FIN标记位被设置为1,1表示为FIN,0表示不是),FIN报文中会指定一个序列号,之后客户端进入FIN_WAIT_1状态

也就是客户端发出连接释放报文段(FIN报文),指定序列号seq = u,主动关闭TCP连接,等待服务器的确认。

第二次挥手:服务器收到连接释放报文段(FIN报文)后,就向客户端发送ACK应答报文,以客户端的FIN报文的序列号 seq+1 作为ACK应答报文段的确认序列号ack = seq+1 = u + 1

接着服务器进入CLOSE_WAIT(等待关闭)状态,此时的TCP处于半关闭状态(下面会说什么是半关闭状态),客户端到服务器的连接释放。客户端收到来自服务器的ACK应答报文段后,进入FIN_WAIT_2状态。

第三次握手:服务器也打算断开连接,向客户端发送连接释放(FIN)报文段,之后服务器进入LASK_ACK(最后确认)状态,等待客户端的确认。

服务器的连接释放(FIN)报文段的FIN=1,ACK=1,序列号seq=m,确认序列号ack=u+1。

第四次握手:客户端收到来自服务器的连接释放(FIN)报文段后,会向服务器发送一个ACK应答报文段,以连接释放(FIN)报文段的确认序号 ack 作为ACK应答报文段的序列号 seq,以连接释放(FIN)报文段的序列号 seq+1作为确认序号ack。

之后客户端进入TIME_WAIT(时间等待)状态,服务器收到ACK应答报文段后,服务器就进入CLOSE(关闭)状态,到此服务器的连接已经完成关闭。

客户端处于TIME_WAIT状态时,此时的TCP还未释放掉,需要等待2MSL后,客户端才进入CLOSE状态


由客户端到服务器需要一个FIN和ACK,再由服务器到客户端需要一个FIN和ACK,因此通常被称为四次握手。

客户端和服务器都可以主动关闭连接,只有率先请求关闭的一方才会进入TIME_WAIT(时间等待状态)。

为什么挥手需要四次?

这是由于TCP的半关闭(half-close)造成的。半关闭是指:TCP提供了连接的一方在结束它的发送后还能接受来自另一端数据的能力。通俗来说,就是不能发送数据,但是还可以接受数据。

TCP不允许连接处于半打开状态时,就单向传输数据,因此完成三次握手后才可以传输数据(第三握手可以携带数据)。

当连接处于半关闭状态时,TCP是允许单向传输数据的,也就是说服务器此时仍然可以向客户端发送数据,等服务器不再发送数据时,才会发送FIN报文段,同意现在关闭连接。

这一特性是由于TCP双向通道互相独立所导致的,也使得关闭连接必须经过四次握手。

可能有些人会有疑惑:为什么中间的ACK和FIN不可以像三次握手那样合为一个报文段呢?

在socket网络编程中,执行close()方法会触发内核发送FIN报文。什么时候调用close()方法,这是由用户态决定的,假如服务器仍有大量数据等待处理,那么服务器会等数据处理完后,才调用close()方法,这个时间可能会很久,而ACK报文则是由系统内核来完成的,这个过程会很快。所以中间的ACK和FIN不能合为一个包。

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

MSL(Maximum Segment LifeTime)是报文最大生成时间,它是任何报文在网络上存在的最长时间,超过这个时间的报文将被丢弃。

因为TCP协议是基于IP协议(位于IP协议的上一层),IP数据报中有限制其生存时间的TTL字段,是IP数据报可以经过的最大路由器的个数,每经过处理它的路由,TTL就会减一。TTL为 0 时还没有到达目的地的数据报将会被丢弃,同时发送 ICMP 报文通知源主机。

MSL的单位为时间,TTL的单位为跳转数。所以MSL应该大于等于TTL变为0的时间,以确保报文已被丢弃。

TIME_WAIT等待的2MSL时间,可以理解为数据报一来一回所需要的最大时间。

2MSL时间是从客户端接收到FIN后发送ACK开始计时的。如果在这个时间段内,服务器没有收到ACK应答报文段,会重发FIN报文段,如果客户端收到了FIN报文段,那么2MSL的时间将会被重置。如果在2MSL时间段内,没有收到任何数据报,客户端则会进入CLOSE状态。

等待2MSL的意义

1.保证客户端最后发送的ACK能够到达服务器,帮助其正常关闭。

由于这个ACK报文段可能会丢失,使得处于LAST_ACK状态的服务器得不到对已发送FIN报文段的确认,从而会触发超时重传。服务器会重发FIN报文段,客户端能保证在2MSL时间内收到来自服务器的重传FIN报文段,从而客户端重新发送ACK应答报文段,并重置2MSL计数。

假如客户端不等待2MSL就之间进入CLOSE状态,那么服务器会一直处于LAST_ACK状态。

当客户端发起建立SYN报文段请求建立新的连接时,服务端会发送RST报文段给客户端,连接建立的过程就会被终止。

2.防止已失效的连接请求报文段出现在本连接中。

TIME_WAIT等待的2MSL时间,确保本连接内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。

TIME_WAIT状态过多有什么危害?

只有主动发起断开请求的一方才会进入TIME_WAIT状态!

1.占用系统资源

2.socket的TIME_WAIT状态结束之前,该socket占用的端口号将一直无法释放。如果服务器TIME_WAIT状态过多,占满了所有端口资源,则会导致无法创建新的连接。

如何解决TIME_WAIT状态过多?

最好的办法是尽量让客户端主动断开连接,除非遇到一些异常情况,如客户端协议错误、客户端超时等。

打开系统的TIME_WAIT重用和快速回收。

在Linux系统可以修改以下参数:

        1.打开TCP对时间戳的支持,保持服务器与客户端时间同步 

net.ipv4.tcp_timestamps=1(默认即为 1)

        2.修改net.ipv4.tcp_tw_reuse = 1,允许对处于TIME_WAIT的socket用于建立新的连接

net.ipv4.tcp_tw_reuse = 1 (默认为0)

修改TIME_WAIT连接状态的上限值,超过上限值,处于TIME_WAIT状态的socket将立刻被清除并打印警告信息。

net.ipv4.tcp_max_tw_buckets = 18000,表示系统同时保持处于TIME_WAIT状态的socket的最大数量,默认为18000。

可修改为更小值。

net.ipv4.tcp_max_tw_buckets = 6666

还有一个是修改短连接为长连接的方式(目前没有学到)。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/137124.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • mysql的反引号怎么打出来_sql注入绕过单引号

    mysql的反引号怎么打出来_sql注入绕过单引号反引号是个比较特别的字符,下面记录下怎么利用0x00SQL注入反引号可利用在分隔符及注释作用,不过使用范围只于表名、数据库名、字段名、起别名这些场景,下面具体说下1)表名payload:select*from`users`whereuser_id=1limit0,1;可以正常执行的,这样使用还可以起到分隔符的作用,如下eg:select*from`users`whereuse…

    2025年6月30日
    0
  • 一个概括性关于维数约简的论文

    一个概括性关于维数约简的论文
    http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.107.1327&rep=rep1&type=pdf

    2022年6月1日
    30
  • 云服务器搭建_免费云服务器

    云服务器搭建_免费云服务器在前一篇的基础上,现在对服务器进行环境搭建,其实就是安装你需要的软件,配置环境变量,然后安装tomcat。环境搭建1、gitbash连接服务器(或者XShell等工具)2、搭建mysql环境

    2022年8月3日
    9
  • 你知道如何从零开始学c++游戏编程吗「建议收藏」

    你知道如何从零开始学c++游戏编程吗「建议收藏」在软件开发中,游戏开发这个方向看起来目标很明确,但其实是个领域很广的方向,入门的时候如果得不到指点一二,很容易误入歧途,相反,如果走这条路之前能得到前人的一些指路,是可以事半功倍的。平台与编程语言选择首先,游戏开发的平台就有很多类型:个人主机平台:Windows、Linux、MacOC;移动平台:iOS、Android、WindowsPhone、BlackBerryOS、Symbian;专业主…

    2022年6月5日
    47
  • archwing任务流程_机器的武器任务流程

    archwing任务流程_机器的武器任务流程有两台机器 A,B 以及 K 个任务。机器 A 有 N 种不同的模式(模式 0∼N−1),机器 B 有 M 种不同的模式(模式 0∼M−1)。两台机器最开始都处于模式 0。每个任务既可以在 A 上执行,也可以在 B 上执行。对于每个任务 i,给定两个整数 a[i] 和 b[i],表示如果该任务在 A 上执行,需要设置模式为 a[i],如果在 B 上执行,需要模式为 b[i]。任务可以以任意顺序被执行,但每台机器转换一次模式就要重启一次。求怎样分配任务并合理安排顺序,能使机器重启次数最少。输入格

    2022年8月10日
    18
  • c语言输出整型量格式符,C语言输出格式(详细)

    c语言输出整型量格式符,C语言输出格式(详细)C语言格式化输出一、printf()函数printf()函数是格式化输出函数,一般用于向标准输出设备按规定格式输出信息。在编写程序时经常会用到此函数。printf()函数的调用格式为:printf(“”,);其中格式化字符串包括两部分内容:一部分是正常字符,这些字符将按原样输出,另一部分是格式化规定字符,以”%”开始,后跟一个或几个规定字符用来确定输出内容格式。参量表是需要输出的一系列参数…

    2022年7月24日
    10

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号