0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

TIME_WAIT状态的优化思路

科技绿洲 来源:Linux开发架构之路 作者:Linux开发架构之路 2023-11-10 10:44 次阅读
  1. 为什么需要TIME_WAIT状态?为什么TIME_WAIT的时长是2*MSL?

原因1:防止连接关闭时四次挥手中的最后一次ACK丢失:

TCP需要保证每一包数据都可靠的到达对端,包括正常连接状态下的业务数据报文,以及用于连接管理的握手、挥手报文,这其中在四次挥手中的最后一次ACK报文比较特殊,TIME_WAIT状态就是为了应对最后一条ACK丢失的情况。

TCP保证可靠传输的前提是收发两端分别维护关于这条连接的状态信息(TCB控制块),当发生丢包时进行ARQ重传。如果连接释放了,就无法进行重传,也就无法保证发生丢包时的可靠传输。

对于最后一条ACK,如果没有TIME_WAIT状态,主动关闭一方(客户端)就会在收到对端(服务器)的FIN并回复ACK后 直接从FIN_WAIT_2 进入 CLOSED状态,并释放连接,销毁TCB实例。此时如果最后一条ACK丢失,那么服务器重传的FIN将无人处理,最后导致服务器长时间的处于 LAST_ACK状态而无法正常关闭(服务器只能等到达到FIN的最大重传次数后关闭)。

至于将TIME_WAIT的时长设置为 2MSL,是因为报文在链路中的最大生存时间为MSL(Maximum Segment Lifetime),超过这个时长后报文就会被丢弃。TIME_WAIT的时长则是:最后一次ACK传输到服务器的时间 + 服务器重传FIN 的时间,即为 2MSL。

原因2:防止新连接收到旧链接的TCP报文:

TCP使用四元组区分一个连接(源端口、目的端口、源IP、目的IP),如果新、旧连接的IP与端口号完全一致,则内核协议栈无法区分这两条连接。

2*MSL 的时间足以保证两个方向上的数据都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定是新连接上产生的。

MSL(Maximum Segment Lifetime) 报文最大生存时间在不同操作系统中的具体值:

Windows : 2 min
Linux(Ubuntu) : 60 s
Unix : 30 s

2. TIME_WAIT对连接并发数的影响(TIME_WAIT过多的危害):

在Linux系统中,MSL = 60 s, 2 * MSL = 120 s,所以一条待关闭的TCP连接会在 TIME_WAIT 状态等待 120秒(2分钟)。

当连接处于TIME_WAIT状态时仍会占用系统资源(fd、端口、内存),当系统的并发连接数很大时,过多的TIME_WAIT状态的连接会对系统的并发量造成影响。

(1)对服务器的影响:

由于服务器一般只需要监听一个固定的端口,所以服务器所能支持的最大并发出数的上限取决于系统套接字描述符fd的大小,以及服务器的内存大小。

fd:

Linux中一个进程 所能打开的fd的最大数量默认为 1024 个,可通过 "ulimit -n (+指定数量)" 进行修改。

Linux系统所能支持的fd最大值在 /proc/sys/fs/fd-max 文件中可以查看,系统当前的fd使用情况可以通过 /proc/sys/fs/fd-nr 查看。(本机上的fd-max的值为:1221842,即100W级。实际上fd的最大值同样也取决于系统内存的大小)

内存:

假设每一个TCP连接需要开辟 “4k的接收缓冲区 + 4k的发送缓冲区 = 8k”,1W的并发连接需要80M内存,10W并发需要800M,100W并发需要8G内存。

综上,服务器的并发数主要受限于系统内存的大小,当 TIME_WAIT 状态的连接过多时,会导致消耗的内存增加,这一点可以通过扩展服务器的内存来解决。

(2)对客户端的影响:

客户端的并发数主要受限于端口数量。

一种典型的场景是:高并发短连接(“短连接”表示“业务处理+传输数据”的时间远远小于TIME_WAIT超时的时间)。

在这种场景下,客户端可能会消耗大量的端口(例如取一个Web网页,1秒钟的HTTP短连接处理完业务数据,却需要 2分钟的TIME_WAIT等待时间,在这段时间内客户端上的这个端口是无法被其他连接使用的,如果新建连接则需要使用另外的端口号),Linux系统的最大端口为65535,除去系统使用的端口号,假设网络进程可使用的端口有 6W个,由于TIME_WAIT状态下在 2*MSL(120秒)内无法再被使用,这就限制了客户端的连接速率为 60000 / 120秒 = 500 次/秒, 这是一个非常低的并发率。

同时,大量的TIME_WAIT连接同样会消耗客户端的内存,所以客户端的最大并发数取决于 端口号与内存 二者中的最小值。

3. 优化TIME_WAIT的方法:

方法1:修改内核参数 tcp_tw_reuse:

net.ipv4.tcp_tw_reuse = 1;
net.ipv4.tcp_timestamp = 1;

注意:tcp_tw_reuse 内核参数只在调用 connect() 函数时起作用,所以只能用于客户端(主动连接的一端)。

tcp_tw_reuse 的作用是:在调用connect()函数时,内核会随机找一个处于TIME_WAIT状态 超过1秒 的连接给新连接复用。(超时时间由 tcp_timestamp设置,默认为 1秒)

这种方式可以缩短 TIME_WAIT 的等待时间。

方法2:修改内核参数 tcp_max_tw_buckets:

net.ipv4.tcp_max_tw_buckets 参数的默认值为18000,当系统中处于 TIME_WAIT 状态的连接数量超过阈值,系统会将后面的TIME_WAIT连接重置。

由于这种方法会直接重置连接,因此需要谨慎使用。

方法3:设置套接字选项 SO_LINGER:

SO_LINGER选项用于设置 调用close() 关闭TCP连接时的行为,注意 SO_LINGER选项会使用RST复位报文段取代 FIN-ACK四次挥手的过程,设置了SO_LINGER选项的一方在调用close() 时会直接发送一个RST,接收端收到后复位连接,不会回复任何响应。

这样做的弊端是导致TCP缓冲区中的数据被丢弃。

正常情况下,调用close后的缺省行为是:如果有待发送的数据残留在发送缓冲区中,内核协议栈将继续将这些数据发送给接收端后才关闭连接,走正常的四次挥手流程;

设置SO_LINGER后,立即关闭连接,通过RST分组,发送缓冲区如果有未发送的数据,将会被丢弃,主动关闭的一方跳过TIME_WAIT状态,直接进入CLOSED(也跳过了FIN_WAIT_1 和 FIN_WAIT_2)。

SO_LINGER的另一种更温和的实现方式是设置一个超时时间(so_linger.l_linger),而不是直接关闭。

应用程序调用close后进入睡眠,内核协议栈负责发送缓冲中残留的待发送数据,如果在 l_linger 超时时间内发送完毕,则走正常的四次挥手流程;如果超时未发送完,则发送RST强制关闭连接,并丢弃发送缓冲区中其余的数据(接收端收到RST后也会丢弃接收缓冲区中的数据),并且发送端close()函数返回 EWOULDBLOCK。

综上,如果只是单纯为了规避 TIME_WAIT 状态,使用 SO_LINGER并不是一个好主意,因为它会在调用close关闭连接时 使用RST强制关闭连接,这可能会导致 发送缓冲区、接收缓冲区 中还未处理完的数据被丢弃。

struct linger so_linger;
so_linger.l_onoff = 1; //0表示关闭,忽略l_linger的值;非0表示打开
so_linger.l_linger = 0; //设置等待时间,等于0则表示立即关闭

setsockopt(fd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));

方法4:设置套接字选项 SO_REUSEADDR:

SO_REUSEADDR 选项用于通知内核:

如果端口忙,并且端口对应的TCP连接状态为TIME_WAIT,则可以重用端口;

如果端口忙,并且端口对应的TCP连接处于其他状态(非TIME_WAIT),则返回 “Address already in use” 的错误信息。

设置SO_REUSEADDR的风险是可能会导致新连接上收到旧连接的数据(复用了旧连接的端口,导致新旧连接的四元组完全一致,内核协议栈无法区分这两个连接)。

SO_REUSEADDR 选项并没像 tcp_tw_reuse 那样同时提供一个 tcp_timestamp 参数可以设置 TIME_WAIT的等待时长。

综上,对TIME_WAIT状态的优化思路是尽量缩小等待时长,而不是暴力的直接关闭(可能会引起新连接收到旧连接数据的风险),也不要直接发送RST复位连接(可能会引起发送、接收缓冲区中的数据丢失),所以使用修改内核参数 tcp_tw_reuse 参数是最保险的方式,通过根据实际网络情况和应用场景适当的调节 tcp_timestamp 的值,可以达到缩小 TIME_WAIT 等待时长,进而减少系统中同一时刻处于 TIME_WAIT 状态的连接数量的目的。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    6514

    浏览量

    87612
  • 操作系统
    +关注

    关注

    37

    文章

    6290

    浏览量

    121901
  • TIME
    +关注

    关注

    0

    文章

    13

    浏览量

    14273
收藏 人收藏

    评论

    相关推荐

    CC3200 socket状态如何查询?

    已,正在等待本地套接字的关闭确认 FIN_WAIT_2 套接字已关闭,正在等待远程套接字关闭TIME_WAIT 这个套接字已经关闭,正在等待远程套接字的关闭传送
    发表于 05-06 11:00

    WEB CLOSE_WAIT socket不释放如何解决呢

    :web连接过后发现有大量的TIME_WAIT状态的socket,怎么限制WEB的并发数量,把rtConfig.h中的这个宏改小到多少合适呢?如果太小了会不会导致web登录不了的情况?#define WEBNET_CONN_MAX 8
    发表于 09-27 10:06

    GPRS优化思路总结报告

    GPRS优化思路总结报告:一、概述 2二、无线优化思路 2三、(E)GPRS网络资源容量分析优化 53.1、
    发表于 07-27 21:29 26次下载

    状态思路在单片机程序设计中的应用

    状态思路在单片机程序设计中的应用 状态机的概念状态机是软件编程中的一个重要概念。比这个概念更重要的是对它的灵活应用。在一个思路清晰而且高
    发表于 02-09 11:25 1w次阅读
    <b class='flag-5'>状态</b>机<b class='flag-5'>思路</b>在单片机程序设计中的应用

    状态思路在单片机程序设计中的应用

    状态思路在单片机程序设计中的应用 状态机的概念       状态机是软件编程中的一个重要概念。比这个概念更重要的是对
    发表于 03-18 15:00 1136次阅读
    <b class='flag-5'>状态</b>机<b class='flag-5'>思路</b>在单片机程序设计中的应用

    GPRS优化思路总结报告_李青春

    (E)GPRS 优化思路通信网络优化,GSM上网,PDCH,EDGEGPRS.
    发表于 01-14 15:21 4次下载

    分析在java中sleep和wait的不同

    他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。 在调用sleep()方法的过程中,线程不会释放对象锁。 而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify
    发表于 09-27 14:41 0次下载

    TCP的这些内存开销原来是这样

    实际中 TCP 连接上肯定是要进行数据的收发的,而且还会有 TIME_WAIT 等其它状态。在这些复杂情况下,一条连接占用多大内存呢?飞哥用做了七天的实验结果告诉你! 实验1:ESTABLISH
    的头像 发表于 02-09 18:08 2210次阅读
    TCP的这些内存开销原来是这样

    服务器产生大量的TIME_WAIT究竟是因为什么

    写在开头,大概 4 年前,听到运维同学提到 TIME_WAIT 状态的 TCP 连接过多的问题,但是当时没有去细琢磨;最近又听人说起,是一个新手进行压测过程中,遇到的问题,因此,花点时间,细深究一下
    的头像 发表于 10-13 16:47 1.3w次阅读
    服务器产生大量的<b class='flag-5'>TIME_WAIT</b>究竟是因为什么

    【GCC编译优化系列】实战分析C代码遇到的编译问题及解决思路

    【GCC编译优化系列】实战分析C工程代码可能遇到的编译问题及其解决思路
    的头像 发表于 07-10 23:15 966次阅读
    【GCC编译<b class='flag-5'>优化</b>系列】实战分析C代码遇到的编译问题及解决<b class='flag-5'>思路</b>

    学习Linux命令的正确姿势

    短时间后,所有的 TIME_WAIT 全都消失,被回收,端口包括服务,均正常。即,在高并发的场景下,TIME_WAIT 连接存在,属于正常现象。
    的头像 发表于 08-31 10:27 379次阅读

    图解Java多线程中的wait()和notify()方法

    wait()和notify()是Object类的方法,用于线程的等待与唤醒,必须搭配synchronized 锁来使用。
    的头像 发表于 03-22 09:29 579次阅读

    TCP和UDP分别是什么 TCP和UDP协议各有什么特点

    在 TCP 四次挥手的最后一步,客户端进入 TIME_WAIT 状态,需要等待一段时间再进入 CLOSED 状态。等待时间通常是两个最大报文段生命周期,即 2MSL,这是为了确保服务器端能够收到客户端发送的最后一个 ACK 报文
    的头像 发表于 08-09 12:34 2868次阅读

    TIME_WAIT是什么

    ,遇到了这道题目: 生产环境 Nginx 后端服务大量 TIME-WAIT 的解决步骤 这里小编给大家做一下系统化、体系化的梳理,使得大家可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”。 基础知识 TIME_WAIT是什么? 在构建TCP客
    的头像 发表于 11-10 14:48 287次阅读
    <b class='flag-5'>TIME_WAIT</b>是什么

    为什么要有TIME_WAIT状态

    首先我们说下状态 TIME_WAIT 出现的原因 TCP的新建连接,断开连接的流程和各个状态,如下图所示 由上图可知:TIME_WAIT 是主动断开连接的一方会出现的,客户端,服务器都
    的头像 发表于 11-13 11:26 437次阅读
    为什么要有<b class='flag-5'>TIME_WAIT</b><b class='flag-5'>状态</b>