TCP 中有一个 TIME_WAIT 状态有了解吗?

一.TIME_WAIT状态如何产生及必要性
从TCP握手协议中,TIME_WAIT状态是主动断开的一方,发送完最后一次ACK之后,进入的状态,并且持续2MSL(max segment lifetime,最大分节生命期)时间长度( MSL 是一个IP数据包能在互联网上生存的最长时间,超过这个时间将在网络中消失)。在这个持续时间内,定义该连接的四元组(客户端IP地址和端口,服务端IP地址和端口号)将不能被使用,发送完ACK之后不进入TIME_WAIT就直接进入CLOSE状态呢?不行的,TCP协议要保证可靠的数据传输。TIME_WAIT状态的必要性:

1.为实现TCP全双工连接的可靠释放

假设发起主动关闭的一方(server)最后发送的ACK在网络中丢失,由于TCP协议的重传机制,执行被动关闭的一方(client)将会重发其FIN,在该FIN到达server之前,server必须维护这条连接状态,否则被动关闭的一方(client)将无法关闭,恢复CLOSED状态。TCP连接所对应的资源(server方的local_ip,local_port)在该时间周期不能被立即释放或重新分配,经过2MSL时间周期没有再收到另一方的FIN之后,该TCP连接才能恢复初始的CLOSED状态。

2.为使旧的数据包在网络因过期而消失

首先假设TCP协议中不存在TIME_WAIT状态的限制,再假设当前有一条TCP连接:(local_ip,local_port,remote_ip,remote_port),因某些原因,我们先关闭,接着很快以相同的四元组建立一条新连接,这种情况下TCP协议栈是无法区分前后两条TCP连接的不同的(TCP连接由四元组唯一标识),中间先释放再建立的过程对其来说是“感知”不到的。这样就会出现问题:前一条TCP连接由local peer发送的数据到达remote peer后,会被该remot peer的TCP传输层当做当前TCP连接的正常数据接收并向上传递至应用层(而事实上,在我们假设的场景下,这些旧数据到达remote peer前,旧连接已断开且一条由相同四元组构成的新TCP连接已建立,因此,这些旧数据是不应该被向上传递至应用层的),从而引起数据错乱进而导致各种无法预知的诡异现象。作为一种可靠的传输协议,TCP必须在协议层面考虑并避免这种情况的发生,这正是TIME_WAIT状态存在的第2个原因。

为什么TIME_WAIT状态持续时间是2MSL?

如果TIME_WAIT状态保存的时间不足够长(小于2MSL),当第一个连接正常终止后,第二个拥有相同的相关五元组的出现时,而第一个连接的重复报文到达(最后的ACK没有被对端收到,被动关闭方重传FIN包),干扰了第二个连接。所以TCP协议为了防止某个连接的重复报文在连接终止后出现,让TIME_WAIT状态持续时间足够长(2MSL),使得相应连接方向上的报文要么被完全接受,要么被丢弃。在建立第二个连接的时才不会发生混淆

二.验证TIME_WAIT状态
首先,开启一个server程序(ip:127.0.0.1;port:8888)监听连接,然后打开三个客户端分别与server建立连接,持续监控8888端口的状态(netstat -nalc | grep ‘8888’),很快连接建立完成;如下图中前7行显示的状态

然后,主动关掉server端程序,client接收到FIN信号,发送ACK后进入CLOSE_WAIT状态(client并未执行close),server接收到ACK后,进入FIN_WAIT2状态;如上图中后6行显示的状态

接着,关闭client,发送FIN到server端,server端接收到FIN信号,发送ACK后进入TIME_WAIT状态,如下图所示server端的状态,然后重新开启一个新的server,建立连接出现时出现"bind error: Address already in use"错误。

从HTTP协议中分析企业中web服务器中出现TIME_WAIT状态的原因:

在HTTP1.1协议中,有个 Connection头,Connection有两个值,close和keep-alive,这个头就相当于客户端告诉服务端,服务端你执行完成请求之后,是关闭连接还是保持连接,保持连接就意味着在保持连接期间,只能由客户端主动断开连接。还有一个keep-alive的头,设置的值就代表了服务端保持连接保持多久,HTTP默认Connection值为close,现在的浏览器发送请求的时候一般都会设置Connection为keep-alive。

三.TIME_WAIT状态引起的危害
  实际上使用长连接(keep-alive)配置不当时,当TIME_WAIT的生产速度远大于其消耗速度时,系统仍然会累计大量的TIME_WAIT状态的连接。如果客户端的TIME_WAIT连接过多,同时它还在不断产生,将会导致客户端端口耗尽,新的端口分配不出来,出现错误。如果服务器端的TIME_WAIT连接过多,可能会导致客户端的请求连接失败。

四.TIME_WAIT状态补救措施与预防
1.内核系统调优:

编辑文件/etc/sysctl.conf,添加如下内容:

net.ipv4.tcp_timestamps = 1

net.ipv4.tcp_tw_reuse = 1

net.ipv4.tcp_tw_recycle = 1

执行 /sbin/sysctl -p 让参数生效。重复上述实验,发现server不会出现TIME_WAIT状态

内核参数的解释如下:

net.ipv4.tcp_syncookies = 1  # 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;

net.ipv4.tcp_timestamps=1 # 开启对于TCP时间戳的支持,RFC 1323 在TCP Reliability一节里,引入了timestamp的TCPoption,两个4字节的时间戳字段,其中第一个4字节字段用来保存发送该数据包的时间,第二个4字节字段用来保存最近一次接收对方发送到数据的时间。有了这两个时间字段,也就有了后续优化的余地。

net.ipv4.tcp_tw_recycle=1  #表示开启TCP连接中TIME_WAIT sockets的快速回收

net.ipv4.tcp_tw_reuse=1 #表示开启重用,允许将TIME_WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;

net.ipv4.tcp_max_tw_buckets=5000 #表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。

net.ipv4.tcp_keepalive_time = 1200 #表示当keepalive起用的时候,TCP发送keepalive消息的频度,缺省是2小时,改为20分钟。

net.ipv4.tcp_fin_timeout=30 # 修改系默认的 TIMEOUT 时间

2.设置socket 的参数/* Enable address reuse*/

通过SO_REUSEADDR设置允许地址重用可以避免出现TIME_WAIT。在服务器端添加代码:

int on = 1;

setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值