logo头像

Always believe youself.

TCP的TIME_WAIT

TIME_WAIT 状态

主动关闭方在收到被动关闭方的FIN包后并返回ACK后,会进入TIME_WAIT状态,TIME_WAIT状态又称2MSL状态。

每个TCP连接都必须有一个最大报文段生存时间MSL,在网络传输中超过这个时间的报文段将被丢弃。

当TCP连接发起一个主动关闭,并发出最后一个ACK时,必须在TIME_WAIT状态停留两倍MSL时间,在2MSL等待期间,定义这个连接的插口(客户端IP地址和端口号,服务器IP地址和端口号的四元组)将不能再被使用。

2MSL状态存在有两个理由:

  1. 允许老的重复报文分组在网络中消逝。
  2. 保证TCP全双工连接的正确关闭。

第一个理由:

假如我们在192.168.1.1:5000和39.106.170.184:6000建立一个TCP连接,一段时间后我们关闭这个连接,再基于相同插口建立一个新的TCP连接,这个新的连接称为前一个连接的化身。老的报文很有可能由于某些原因迟到了,那么新的TCP连接很有可能会将这个迟到的报文认为是新的连接的报文,而导致数据错乱。为了防止这种情况的发生TCP连接必须让TIME_WAIT状态持续2MSL,在此期间将不能基于这个插口建立新的化身,让它有足够的时间使迟到的报文段被丢弃。

第二个理由:

因为如果主动关闭方最终的ACK丢失,那么服务器将会重新发送那个FIN,以允许主动关闭方重新发送那个ACK。要是主动关闭方不维护2MSL状态,那么主动关闭将会不得不响应一个RST报文段,而服务器将会把它解释为一个错误,导致TCP连接没有办法完成全双工的关闭,而进入半关闭状态。

为什么是维持2MSL

  • 一个MSL是确保主动关闭方最后的ACK能够到达对端。
  • 一个MSL是确保被动关闭方重发的FIN能够被主动关闭方收到。

处理 TIME_WAIT 状态

一个web服务器最大的端口数量是65535个,如果这个服务器作为客户端不停的和服务端不停的创建短连接,就会导致有大量的TCP进入TIME_WAIT状态。

在RFC793中规定MSL的时间为2min,在实际使用中一般是30s或者1min,在高并发的情况下毫无疑问,这将造成大量连接无法建立的问题,那么有什么方法可以处理这些问题那?

  • SO_REUSEADDR 1: 允许套接字端口服用, 0 : 不允许
    -TCP头部时间戳选项(TCP Timestamps Option),时间戳可选项主要包含4个部分:kind:类型,length:长度,TimeStamp value:发送方时间戳,TimeStamp echo reply:回显时间戳
  • net.ipv4.tcp_tw_reuse
  • net.ipv4.tcp_tw_recycel

TCP 可靠的四次挥手关闭链接过程

image

通过此图先说明几个概念:

  • TIME_WAIT的产生条件:主动关闭方在发送四次挥手的最后一个ACK会变为TIME_WAIT状态,保留次状态的时间为两个MSL(linux里一个MSL为30s,是不可配置的)

  • TIME_WAIT两个MSL的作用:可靠安全的关闭TCP连接。比如网络拥塞,主动方最后一个ACK被动方没收到,这时被动方会对FIN开启TCP重传,发送多个FIN包,在这时尚未关闭的TIME_WAIT就会把这些尾巴问题处理掉,不至于对新连接及其它服务产生影响。

  • TIME_WAIT占用的资源:少量内存(查资料大概4K)和一个fd。

  • TIME_WAIT关闭的危害:

    • 网络情况不好时,如果主动方无TIME_WAIT等待,关闭前个连接后,主动方与被动方又建立起新的TCP连接,这时被动方重传或延时过来的FIN包过来后会直接影响新的TCP连接;

    • 同样网络情况不好并且无TIME_WAIT等待,关闭连接后无新连接,当接收到被动方重传或延迟的FIN包后,会给被动方回一个RST包,可能会影响被动方其它的服务连接。

  • TCP: time wait bucket table overflow产生原因及影响:原因是超过了linux系统tw数量的阀值。危害是超过阀值后﹐系统会把多余的time-wait socket 删除掉,并且显示警告信息,如果是NAT网络环境又存在大量访问,会产生各种连接不稳定断开的情况。