zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    JVM探究之 —— HotSpot虚拟机对象探秘
    JVM探究之 —— Java内存区域
    线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式
    dubbo线程模型配置
    hash bucket
    java 查看线程的信息
    List,Set,Map存取元素各有什么特点
    java 让图片变黑白
    springMVC 防重校验(拦截器)
    BigDecimal 的幂次方运算
  • 原文地址:https://www.cnblogs.com/guoxianqi2020/p/13722983.html
Copyright © 2011-2022 走看看