现象:有某种上网方式的用户经常不能连接到服务器。
简单来说,tcp_tw_recycle 机制允许协议不需要真的等待2个最大段生存时间MSL 那么长,就可以关闭一个连接了,只需要等待2个数据包来回时间,这个相对很短,所以TIME_WAIT状态的连接就可以及时回收了,免得占用系统资源。但2*MSL改为了2*RTT, 那么问题很明显,可能出现数据包错乱,比如被动关闭一方的FIN迟迟没有到来,服务器这边会回收这个连接,然后之后的新连接可能就会复用了这个端口port信息,然后突然之间客户端的老的FIN到达了服务器,然后服务器以为这个FIN包对应的Port正好是刚刚建立的新连接的一个FIN包,于是服务器就把新连接给干掉了·····
也就是说,本身TCP协议规定了,主动关闭一方必须等待2*MSL 的时间,可能长达几分钟,但你现在改为2*RTT,明显短太多了,会出问题的。所以怎么办呢?
由于我们在/proc/sys/net/ipv4/tcp_tw_recycle设置后,会清除掉TCP的四元组信息,释放内存,所以没法进行端口判断了,但是IP还是可以的,所以退而求其次:比较这个IP的最新更新的时间戳,如果碰到一个很老的时间戳TSVal的数据包过来,那么服务器认为这应该是之前很老的数据包的重传,只能忽略它。因此实践中协议采取的策略是:60秒内同一个IP建立2个连接的话,后面一个SYN连接的时间戳必须大于之前的SYN里面的TSVal,否则服务器会认为这是一个老连接的数据包,忽略它。
从而导致客户端明明发送了SYN,服务端却偏偏就是不回复SYN-ACK。
类似的不少人碰到,就不多重复了。解决方法一般就是关闭/proc/sys/net/ipv4/tcp_tw_recycle 就OK了,不用这么激进的去回收TIME_WAIT。
注意这个状态只在主动关闭的一方才会出现,但是很多不明真相的同学总会喜欢不管如何,都设置这个参数,其实像web服务器这样的,开启了keepalive的服务器,是不需要去care这个状态的,因为一般都是客户端主动关闭连接,所以是客户端的责任区处理TIME_WAIT。