TIME_WAIT状态

1. TIME_WAIT状态

主动关闭方在收到被动关闭方的FIN包后并返回ACK后,会进入TIME_WAIT状态,TIME_WAIT状态又称2MSL状态,每一个TCP链接都必须有一个最大报文段生存时间MSL,在网络传输中超过这个时间的报文段将被丢弃。当TCP链接发起一个主动关闭,并发出最后一个ACK时,必须在TIME_WAIT状态停留两倍MSL时间,在2MSL等待期间,定义这个链接的插口(客户端IP地址和端口号,服务器IP地址和端口号的四元组)将不能再被使用。2MSL状态存在有两个理由:html

  • 1.容许老的重复报文分组在网络中消逝。java

  • 2.保证TCP全双工链接的正确关闭。linux

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

第二个理由是由于若是主动关闭方最终的ACK丢失,那么服务器将会从新发送那个FIN,以容许主动关闭方从新发送那个ACK。要是主动关闭方不维护2MSL状态,那么主动关闭将会不得不响应一个RST报文段,而服务器将会把它解释为一个错误,致使TCP链接没有办法完成全双工的关闭,而进入半关闭状态。编程

1.1 为何是维持2MSL

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

2.处理TIME_WAIT状态

一个web服务器最大的端口数量是65535个,若是这个服务器做为客户端不停的和服务端不停的建立短链接,就会致使有大量的TCP进入TIME_WAIT状态。缓存

当服务端是主动关闭方,由于有TIME_WAIT状态的存在,在重启程序的时候可能会出现java.net.BindException: Address already in use的错误,这是由于这个端口TIME_WAIT状态须要等待2MSL。在RFC793中规定MSL的时间为2min,在实际使用中通常是30s或者1min,在高并发的状况下毫无疑问,这将形成大量链接没法创建的问题,那么有什么方法能够处理这些问题那?服务器

2.1 SO_REUSEADDR

  • SO_REUSEADDR设置为1在 TIME_WAIT时容许套接字端口复用。
  • SO_REUSEADDR设置为0 TIME_WAIT时不容许容许套接字端口复用。

在Java中能够经过如下代码设置:微信

ServerSocket serverSocket = new ServerSocket();
// setReuseAddress 必须在 bind 函数调用以前执行
serverSocket.setReuseAddress(true);
复制代码

SO_REUSEADDR能够用在如下四种状况下。(摘自《Unix网络编程》卷一,即UNPv1)网络

  1. 当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。
  2. SO_REUSEADDR容许同一port上启动同一服务器的多个实例(多个进程)。但每一个实例绑定的IP地址是不能相同的。在有多块网卡或用IP Alias技术的机器可 以测试这种状况。
  3. SO_REUSEADDR容许单个进程绑定相同的端口到多个 socket上,但每一个 socket绑定的ip地址不一样。这和2很类似,区别请看 UNPv1
  4. SO_REUSEADDR容许彻底相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。

因此SO_REUSEADDR并非在全部状况下均可以使用的。并发

参考:TCP套接字端口复用SO_REUSEADDR

2.2 TCP头部时间戳选项(TCP Timestamps Option)

时间戳可选项主要包含4个部分:

  1. kind:类型
  2. length:长度
  3. TimeStamp value:发送方时间戳
  4. TimeStamp echo reply:回显时间戳

时间戳可选项能够处理序号回绕,判断乱序,更加准确的计算RTT,在linux中能够经过net.ipv4.tcp_timestamps设置,通常默认是1(打开)。

发送方在发送数据时将发送数据的时间X放到发送方时间戳TSval中。

接收方在接收到数据的时候将收到的时间X原封不动的放到回显时间戳TSecr中,同时将本身发送数据的时间Z放到发送方时间戳TSval中,以此类推。

2.3 net.ipv4.tcp_tw_reuse

在linux中tcp_tw_reuse默认为0,设置为1后表示客户端容许TIME_WAIT状态重用。重用的条件是:

  1. tcp_tw_reuse选项和 tcp_timestamps也必须同时打开;
  2. 开启后收到最后一个包后超过1s。

当开启tcp_tw_reuse后,主动关闭方收到包的时间戳比存储的时间戳小,则证实收到的包是一个旧的链接的包,直接丢弃。

2.4 net.ipv4.tcp_tw_recycel

在linux中tcp_tw_recycel默认为0,设置为1后开启(tcp_timestamps必须同时打开),开启tcp_tw_recycel后服务器将会缓存每一个套接字的最新时间戳。对于新的链接,若是SYN包中的时间戳小于以前缓存的套接字的时间戳,则直接丢弃,不然容许复用TIME_WAIT

这种机制在通过NAT或者负载均衡后,会发生严重的问题。由于不一样请求通过NAT或者负载均衡后IP多是同样的,就会致使TIME_WAIT没法复用。因此不推荐打开这个参数。

关注做者微信公众号:

相关文章
相关标签/搜索