下面是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状态是比较保险的。
当服务器上存在大量连接的时候,TIME_WAIT状态就会变得比较麻烦,连接表里有大量处于TIME_WAIT状态的连接,会导致新的连接不能够建立,同时它还会占用系统资源,这个就会限制服务器能处理的连接数目。
对于两个服务器之间处理数据, 它们的目标地址和目标端口可能是不变的, 在Linux上,默认情况下动态端口范围是49152~65535, 这也就意味着每分钟服务器之间能建立的连接是很有限的, 一旦被占用就不能投入使用了。
最好的解决办法还是增加更多的四元组组合(源IP,源端口, 目的IP,目的端口), 不过按照之前提到的,在更严格的限制下, 处于TIME_WAIT状态下的本地端口都不能再使用了。所以最好的不出错的解决方案是:
1、 通过修改net.ipv4.ip_local_port_range来增加客户端端口数目
2、 部署更多的客户端程序,能有更多的客户端IP。
有些时候可能会通过调整net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle来尝试解决这个问题,但是这个都是有风险的,接下来会对这几个参数进行介绍。
和这方面相关的参数有以下几个:
这个参数指定了在套接字强行关闭之前,等待最终FIN数据包的秒数, 对应的是主动关闭端FIN_WAIT-2的状态, 默认值是60s。通过修改这个值也可以缩短连接关闭时间,这个和TIME_WAIT的持续时间无关。FIN_WAIT-2套接字最多需要1.5k的内存,如果调长这个值,有可能的风险是大量套接字处于这个状态导致内存溢出,虽然单位占用内存少, 但是持续的时间长。
这个参数可以被用于保障TCP传输的可靠性即用于PAWS机制,防范在高带宽下,TCP序列号在较短时间内就被重复使用,同一条TCP流在短时间内出现序号一样的两个合法数据包及其确认包。服务也可以通过包里带的时间戳,更加准确的计算RTT。
tcp_timestamp参数的默认取值为1, 表示收发包使用时间戳。 它的取值在Linux 4.10后的版本里做了些修改, 0表示关闭时间戳功能, 1 表示在收发包时不仅利用当前时间戳,还会利用每个连接生成的随机偏移量,2 表示只使用当前时间戳。
如果要使用后面两个参数,这个值需要置为非0。
*** 当tcp连接发起方处于NAT网络中, 向同一服务器发起请求时, 每个设备的时间戳不一定会递增, 有可能会导致有些连接一直被rst, 连接不上, 所以在NAT网络中的服务器开启timestamp有风险。
为了提高高带宽下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服务异常。
启用这个选项,能够更快速的回收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
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有