TCP(二)TIME_WAIT

一、 什么是timewait?
Timewait是TCP连接中,四次挥手时出现的一个状态,在主动关闭方发出最后一个ACK后,就会进入timewait状态,并等待2MSL时间后,进入CLOSE状态。

二、 MSL
MSL(Maximum Segment Lifetime),报文最大生存时间,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
在linux操作系统中,是30秒。这个数值是硬编码在内核中的,也就是说除非你重新编译内核,否则没法修改它
客户端发起http请求,并主动关闭后,可以看到linux中2msl时间就是60s,timewait (59.98/0/0)

# curl "http://10.30.20.80"&&netstat -antp -o|grep TIME_WAIT
tcp  0      0 10.30.20.91:30078       10.30.20.80:80 TIME_WAIT   -  timewait (59.98/0/0)

三、 为什么需要timewait状态?
1、 确保被动关闭方已经关闭了连接
当主动关闭方发出最后的ACK后,如果由于某种异常导致报文丢失,被动方没有收到最后的ACK报文会一直处于LAST-ACK状态,无法进入CLOSED状态。
假设主动关闭方跳过TIME_WAIT状态或者处于TIME_WAIT状态很短的时间后进入CLOSED状态,此时主动关闭方如果使用相同的源端口,发起SYN建连请求,被动关闭方由于还处于LAST_ACK状态,收到SYN包,此时就会回复RST包,导致新连接无法正常建立起来。

2、 新的TCP连接被建立起来了,延迟包可能干扰新的连接
当使用原来的五元组来建立新的TCP连接,如果上一次连接还有数据报文,由于网络拥塞等原因,在新连接建立后才到达(且序列号一致),此时就会干扰到新的连接了,当然出现这种问题的概率比较低。

四、 处于Last_ack状态的数据包,什么时候关闭?

假设主动关闭方为A,被动关闭方为B
1、B发送FIN后,进入LAST_ACK状态,A收到FIN包后发送ACK包,B收到这个ACK后,进入CLOSED状态。
2、B发送FIN后,进入LAST_ACK状态,A收到FIN包后发送ACK包,由于某种原因,这个ACK包丢失,B没有收到ACK包,然后B等待ACK包超时,又向A发送了一个FIN包。
1)假设A还处于TIME_WAIT状态,A收到这个FIN包后向B发送了一个ACK包,B收到这个ACK包进入CLOSED状态。
2)假设A已经处于CLOSED状态,A收到这个FIN包后,认为这是一个错误的连接,向B发送一个RST包,当B收到这个RST包,进入CLOSED状态。
3)假设A宕机,B没有收到A的回应,那么会继续发送FIN包,也就是触发了TCP的重传机制,如果A还是没有回应,B还会继续发送FIN包,直到重传超时,B重置这个连接,进入CLOSED状态。

五、timewait优化
timewait优化的可以从如下维度进行调整
1、从五元组的维度
源地址、源端口、目的地址、目的端口、协议
1)源地址
增加客户端的IP地址
2)源端口
设置内核参数net.ipv4.ip_local_port_range,调整可用的端口范围,由于只有16位,最多只能有65535,调整可用的端口范围,1-65535
3)目的地址
增加服务端的IP地址
4)目的端口
增加服务端的监听端口

2、缩短回收时间或重用维度
1)tcp_max_tw_buckets
设置存储的buckets大小,超过指定的大小,将丢弃。从timewait的数量来说,指定了timewait数量的上限,但是为了保证tcp的正常关闭,应该调大
2)tcp_tw_reuse
重用tcp连接,只对连接发起方有效
3)tcp_tw_recycle
缩短timewai的回收时间为3*RTO,但是会有一定几率影响连接建立,生产上建议还是不要开启

六、几个内核参数的说明

1、tcp_timestamp

开启TCP的timestamp的option,两个4字节的时间戳字段,其中第一个4字节字段用来保存发送该数据包的时间,第二个4字节字段用来保存最近一次接收对方发送到数据的时间戳。

 2、tcp_tw_recycle

开启后,缩短time_wait的回收时间,回收时间为3*RTO(Retransmission Timeout),RTO 时间在200ms~ 120s 具体时间视网络状况。

# ss --info sport = :64707 dport = :58625
Netid State      Recv-Q Send-Q                                                               Local Address:Port                                                                                Peer Address:Port                
tcp   ESTAB      0      0                                                                      10.30.20.80:64707                                                                                 10.31.4.13:58625                
         cubic wscale:2,7 rto:201 rtt:0.273/0.06 ato:40 mss:1448 rcvmss:1448 advmss:1448 cwnd:10 bytes_acked:9440165 bytes_received:3070 segs_out:14177 segs_in:10034 send 424.3Mbps lastsnd:7478 lastrcv:60707483 lastack:7477 pacing_rate 846.3Mbps reordering:5 rcv_rtt:1 rcv_space:29200

当多个客户端通过NAT方式联网并与服务端交互时,服务端看到的是同一个IP,也就是说对服务端而言这些客户端实际上等同于一个,可惜由于这些客户端的时间戳可能存在差异,于是乎从服务端的视角看,便可能出现时间戳错乱的现象,进而直接导致时间戳小的数据包被丢弃。如果发生了此类问题,具体的表现通常是是客户端明明发送的SYN,但服务端就是不响应ACK,我们可以通过下面命令来确认数据包不断被丢弃的现象。
补充:在测试中,使用在同一局域网的两台服务器之间互访,开启参数后,都有一定的几率由于时间戳导致无法正常建立连接。

客户端:10.30.20.108
服务端:10.30.20.80
测试命令:ab -c 1000 -n 600000 http://10.30.20.80/
内核版本:3.10.0-1062.9.1.el7.x86_64
场景一、
客户端参数:
net.ipv4.tcp_timestamps=1
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_tw_reuse = 0
服务端:
net.ipv4.tcp_timestamps=1
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_tw_reuse = 0
结果:
# netstat -s|grep "time stamp"  # 没有因为时间戳导致的连接建立异常
# ss -s
Total: 213 (kernel 406)
TCP:   60571 (estab 4, closed 60561, orphaned 0, synrecv 0, timewait 60561/0), ports 0

Transport Total     IP        IPv6
*         406       -         -        
RAW       0         0         0        
UDP       1         1         0        
TCP       10        8         2        
INET      11        9         2        
FRAG      0         0         0

场景二
客户端参数:
net.ipv4.tcp_timestamps=1
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_tw_reuse = 0
服务端:
net.ipv4.tcp_timestamps=1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 0
结果:
# netstat -s|grep "time stamp";ss -s
    35 passive connections rejected because of time stamp # 出现时间戳导致的连接建立异常
Total: 215 (kernel 419)
TCP:   11 (estab 4, closed 1, orphaned 0, synrecv 0, timewait 1/0), ports 0

Transport Total     IP        IPv6
*         419       -         -        
RAW       0         0         0        
UDP       3         2         1        
TCP       10        8         2        
INET      13        10        3        
FRAG      0         0         0

3、tcp_tw_reuse
tcp_tw_reuse,就是重用处于TIME-WAIT状态的sockets来建立新连接,也就是说在客户端起作用。
重用TIME_WAIT的条件是收到最后一个包后超过1s,就可以开始重用这个socket。

原文地址:https://www.cnblogs.com/guoxianqi2020/p/13722983.html