TCP TIME_WAIT

TCP TIME_WAIT状态理解:

下面是tcp状态图(来自下面的参考文章):

从图中可以看出,若服务器主动关闭连接,在四次挥手的最后一个ACK后连接端口会变为TIME_WAIT状态, 状态停留时长为两个MSL(最大分段寿命),这个状态只有在主动关闭连接方会出现, 另一端可以在连接断开后立刻投入后续使用。

MSL是一个TCP分段可以存在于互联网系统中的最大时长,RFC 793指出MSL为2分钟, 但在LINUX系统中一般为30s,通过下面这个命令可以确定一些LINUX系统上的MSL数值:

sysctl net.ipv4.tcp_fin_timeout

当TCP连接处于TIME_WAIT这个状态时,标准中定义这个连接的插口(源Ip, 源端口,目的Ip,目的端口)不能再被使用,任何迟到的报文段都会被丢弃。 而大多数的TCP实现强加了更严格的限制, 在TIME_WAIT状态下,处于这个连接的本地端口默认情况下都不能再被使用,同时为了防止处于TIME_WAIT端口的主机出现故障,重启后马上建立新连接, RFC793有规定, TCP在重启动后的MSL秒内不能建立任何连接。

TIME_WAIT状态限制是比较严格的,设置TIME_WAIT状态主要有两个目的:

1、为了防止一个连接的延迟分段被后面新建的连接接收。连接是通过四元组(源Ip, 源端口,目的Ip,目的端口)来确定的, 虽然延迟包的序列号要满足新建连接的要求才能被接受,但是如果没有TIME_WAIT状态,新的连接还是有可以接收到上一个连接的延迟包,这个在RFC1337中有定义。

2、为了确保远程端已关闭连接。关闭端口的最后一个ACK有可能会丢失, 另一端可能会超时并重发最后的FIN, 保持TIME_WAIT状态可以让TCP再次发送最后的ACK。另外如果没有TIME_WAIT状态的话, 可以重新打开连接, 远程端可能会认为这个连接仍然是有效果的, 但它收到序列号匹配的SYN时, 它会回RST终止新连接并显示错误。

从上述两点原因来看, 有TIME_WAIT状态是比较保险的。


TCP TIME_WAIT可能出现问题以及参数调整:

当服务器上存在大量连接的时候,TIME_WAIT状态就会变得比较麻烦,连接表里有大量处于TIME_WAIT状态的连接,会导致新的连接不能够建立,同时它还会占用系统资源,这个就会限制服务器能处理的连接数目。

对于两个服务器之间处理数据, 它们的目标地址和目标端口可能是不变的, 在Linux上,默认情况下动态端口范围是49152~65535, 这也就意味着每分钟服务器之间能建立的连接是很有限的, 一旦被占用就不能投入使用了。

最好的解决办法还是增加更多的四元组组合(源IP,源端口, 目的IP,目的端口), 不过按照之前提到的,在更严格的限制下, 处于TIME_WAIT状态下的本地端口都不能再使用了。所以最好的不出错的解决方案是:

1、 通过修改net.ipv4.ip_local_port_range来增加客户端端口数目

2、 部署更多的客户端程序,能有更多的客户端IP。

有些时候可能会通过调整net.ipv4.tcp_tw_reusenet.ipv4.tcp_tw_recycle来尝试解决这个问题,但是这个都是有风险的,接下来会对这几个参数进行介绍。

和这方面相关的参数有以下几个:

l net.ipv4.tcp_fin_timeout

这个参数指定了在套接字强行关闭之前,等待最终FIN数据包的秒数, 对应的是主动关闭端FIN_WAIT-2的状态, 默认值是60s。通过修改这个值也可以缩短连接关闭时间,这个和TIME_WAIT的持续时间无关。FIN_WAIT-2套接字最多需要1.5k的内存,如果调长这个值,有可能的风险是大量套接字处于这个状态导致内存溢出,虽然单位占用内存少, 但是持续的时间长。

l net.ipv4.tcp_timestamps

这个参数可以被用于保障TCP传输的可靠性即用于PAWS机制,防范在高带宽下,TCP序列号在较短时间内就被重复使用,同一条TCP流在短时间内出现序号一样的两个合法数据包及其确认包。服务也可以通过包里带的时间戳,更加准确的计算RTT。

tcp_timestamp参数的默认取值为1, 表示收发包使用时间戳。 它的取值在Linux 4.10后的版本里做了些修改, 0表示关闭时间戳功能, 1 表示在收发包时不仅利用当前时间戳,还会利用每个连接生成的随机偏移量,2 表示只使用当前时间戳。

如果要使用后面两个参数,这个值需要置为非0。

*** 当tcp连接发起方处于NAT网络中, 向同一服务器发起请求时, 每个设备的时间戳不一定会递增, 有可能会导致有些连接一直被rst, 连接不上, 所以在NAT网络中的服务器开启timestamp有风险。

l net.ipv4.tcp_tw_reuse

为了提高高带宽下tcp的性能, RFC1323中定义了一个新的TCP选项, 包含了两个4字节时间戳字段, 第一个字段存储的是tcp发送选项时的当前时钟时间戳, 另一个字段存储的是从远端接收到的最新时间戳。

通过启用tcp_tw_reuse,如果新时间戳严格大于先前连接记录的时间戳, linux可以重复使用在TIME_WAIT状态的已有连接作为传出连接,发送数据, 这个传出连接在连接变为TIME_WAIT状态的1s之后就可以重用。

因为使用了时间戳, 重复的延迟包会带有过期的时间戳, 就会被丢弃, 因此避免了延迟包被新连接接收。此外也可以避免远程端丢失最后一个关闭连接的ACK,处于LASK-ACK状态而一直重发FIN,因为新建立的连接会发送一个SYN包, 它收到FIN包以后可以发送RST, 让远程端结束LASK-ACK状态。

这种方式对于传入连接没有什么帮助。

*** tcp_tw_reuse开启同样也有风险,正因为使用了时间戳,带着过期时间戳的包会被丢弃,因此同样会有上述所说问题, 在NAT网络中的客户端,有可能由于有些机器的时间戳远小于其他机器,导致tcp服务异常。

l net.ipv4.tcp_tw_recycle

启用这个选项,能够更快速的回收TIME_WAIT套接字,这种机制也依赖时间戳选项,它将会在超时重传(RTO)间隔后移除TIME_WAIT状态。

为了保证开启选项后, 也能达到TIME_WAIT状态同样的效果,它会记录远程端发来数据的最新时间戳,在TIME_WAIT状态生效期内,放弃所有时间戳小于记录时间戳的包。

*** 如果远端服务器处于NAT网络中, NAT网络中设备的机器时间戳可能不是统一的, 有可能会造成一些机器无法正常和远端通信,所有包都被丢弃,因此最好不要开启这个选项。

目前这个选项已经在Linux4.12以后的版本里被移除了。

参考文章:

https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

网络 后台

1 篇文章1 人订阅

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏idealclover的填坑日常

Linux 下尝试自建Anki服务器

关于Anki是什么,具体怎么用,也许以后会在公众号中做进一步的介绍。总的来说,Anki是一个很好用的,在电脑端手机端均可用的,开源的记忆应用。可以帮助自己记住包...

1K20
来自专栏梦魇小栈

Ubuntu 使用 Cron 实现计划任务

Windows 自带定时执行任务的工具叫做“计划任务”,Linux 下我们使用 Cron 实现这一功能。

42720
来自专栏idealclover的填坑日常

从零开始折腾博客(3):一键安装脚本LAMP.sh搭建Wordpress站点

LAMP:Linux+Nginx+MySQL+PHP,为动态网站的搭建提供了一系列的平台支持。

68040
来自专栏封碎

linux下用adb连接手机的问题解决 博客分类: Android LinuxAndroidEclipseBash

  在linux里面,模拟器可以直接识别,使用adb也没有限制,但是手机插上usb之后,adb并不识别,显示的是问号,在eclipse里面也是这样。

20820
来自专栏醉生梦死

Centos6.9系统部分基础优化(更新时间2018/04/19)

注:此博客仅供参考,读者可根据自己的实际情况进行合理的配置,博客内容参考老男孩书籍《web集群实战》一书

30730
来自专栏idealclover的填坑日常

从零开始折腾博客(2):LMAP搭建Wordpress博客

上面安装的只是MySQL的支持组件,其中的MySQL的系统默认使用的是MariaDB。具体的原因MySQL因为被Oracle收购,有潜在的闭源可能性。为了防止意...

23520
来自专栏咸鱼不闲

科大讯飞语音识别和语音播放dome

首先登陆科大讯飞开发者平台,注册账号,(走你->http://www.xfyun.cn/) 可以根据功能(语音识别,语音播放等),平台(java,window等...

1.7K50
来自专栏封碎

ubuntu升级之后启动不了的解决 博客分类: Linux UbuntuLinuxDebianAndroidEclipse

我的ubuntu是用wubi安装的,今天准备搞android的ndk,所以把开发环境给配置了一下,jdk、android的sdk、ndk,还有eclipse...

11120
来自专栏封碎

NDK入门、提高和实战 博客分类: Android AndroidLinuxJNIEclipseC#

    网上也有一些对NDK的介绍,不过都是很简单的把sample里面的例子讲解一下,并不深入,我这里把我的所得分享一下。我下载的是Android Native...

15060
来自专栏宝哥的专栏

Docker系列学习文章 - 网络基本配置(九)

| 导语 上一篇我们讲解了docker的存储配置,那么这篇文章我们讲下docker的网络。存储解决了docker存东西的问题,网络了解后我们就能知道容器如何连...

1.5K90

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励