在Linux网络编程中,我相信大多数人觉得最难理解的就是TCP中的TIME_WAIT状态了吧,那么TIME_WAIT的概念到底是什么,有几个类型呢,以及在面试中经常会问到的TIME_WAIT状态产生的原因,危害,如何避免,在这篇文章中我会带着大家一起详细解读一下。
1)什么是TIME_WAIT状态
2)TIME_WAIT状态的类型
3)TIME_WAIT状态产生的原因
4)TIME_WAIT状态的危害
5)如何避免TIME_WAIT状态
什么是TIME_WAIT状态
从这幅状态图中可以看到,通信双方建立TCP连接后,在发生四次挥手之后,先发FIN标志位的一端就会进入time_wait状态,换句话说,主动关闭连接的一方就会进入TIME_WAIT状态。
以下图为例,客户端主动关闭连接时,发送最后一个ACK后,然后会进入TIME_WAIT状态,再停留2个MSL时间(后有MSL的解释),进入CLOSED状态,所以我们可以粗略地理解成在断开连接后主动断开连接的那一方就会进入一个等待状态,过了一定时间后,再真正的关闭,这所谓的状态也就是TIME_WAIT状态,这其中所说的一定时间也就是2MSL(MSL:最长分节生命期maximum segment lifetime,这个状态会持续MSL时长的两倍,所以称为2MSL)。
TIME_WAIT状态的类型
Time_wait分为两类:主动型time_wait和被动型time_wait。
主动型:本机主动与其他服务器建立连接时的time_wait,这种情况下本机产生的随机端口号较多。
被动型:本机由于其他服务器主动连接过来后,产生的time_wait。这种情况下,本机ip对应开启的端口号相对固定。
TIME_WAIT状态产生的原因
TIME_WAIT状态存在的理由有两个:
1)可靠地实现TCP全双工连接的终止
由TCP状态变迁图可知,假设发起主动关闭的一方(client)最后发送的ACK在网络中丢失,由于TCP协议的重传机制,执行被动关闭的一方(server)将会重发其FIN,在该FIN到达client之前,client必须维护这条连接状态,也就说这条TCP连接所对应的资源(client方的local_ip,local_port)不能被立即释放或重新分配,直到另一方重发的FIN达到之后,client重发ACK后,经过2MSL时间周期没有再收到另一方的FIN之后,该TCP连接才能恢复初始的CLOSED状态。如果主动关闭一方不维护这样一个TIME_WAIT状态,那么当被动关闭一方重发的FIN到达时,主动关闭一方的TCP传输层会用RST包响应对方,这会被对方认为是有错误发生,然而这事实上只是正常的关闭连接过程,并非异常。
2)允许老的重复分节在网络中消逝
为理解存在TIME_WAIT状态的第二个理由,我们假设在12.106.32.254的1500端口和206.168.112.219的21端口之间有一个TCP连接。我们关闭这个连接,过一段时间后在相同的IP地址和端口之间建立另一个连接。后一个连接称为前一个连接的化身(incarnation),因为它们的IP地址和端口号都相同。TCP必须防止来自某个连接的老的重复分组在该连接已终止后再现,从而被误解成属于同一连接的某个新的化身。为做到这一点,TCP将不给处于TIME_WAIT状态的连接发起新的化身。既然TIME_WAIT状态的持续时间是MSL的2倍,这就足以让某个方向上的分组最多存活MSL秒即被丢弃,另一个方向上的应答最多存活MSL秒也被丢弃。通过实施这个规则,我们就能保证每成功建立一个TCP连接时,来自该连接先前化身的老的重复分组都已在网络中消逝了。
具体而言,local peer主动调用close后,此时的TCP连接进入TIME_WAIT状态,处于该状态下的TCP连接不能立即以同样的四元组建立新连接,即发起active close的那方占用的local port在TIME_WAIT期间不能再被重新分配。由于TIME_WAIT状态持续时间为2MSL,这样保证了旧TCP连接双工链路中的旧数据包均因过期(超过MSL)而消失,此后,就可以用相同的四元组建立一条新连接而不会发生前后两次连接数据错乱的情况。
TIME_WAIT状态的危害
不同Time_wait对系统性能的影响是不同的。
被动型time_wait一般由于被动建立链接产生的time_wait,这种情况下一般占用的端口号比较少,产生的time_wait数也很少。几乎可以忽略不计,对服务器性能几乎没有任何影响。
主动型time_wait对服务器的影响比较大。因为服务器可利用的端口号是有限的, 也就是说建立的连接数是有限的,一般都是有个阈值,超过这个阈值,就会无法建立链接,导致访问失败,如果占用达到服务器端口号极限,对服务器的性能就会产生影响。
假设处理完这100万的数据,就会产生500万次连接,一般机器的连接数上限是65533,如果每个TCP请求都得等60s才能断开,那么500万的请求肯定是无法承受的,就无法建立链接,导致很多请求发不出去 。
如何避免TIME_WAIT状态
首先服务器可以设置SO_REUSEADDR套接字选项来通知内核,如果端口忙,但TCP连接位于TIME_WAIT状态时可以重用端口。在一个非常有用的场景就是,如果你的服务器程序停止后想立即重启,而新的套接字依旧希望使用同一端口,此时SO_REUSEADDR选项就可以避免TIME_WAIT状态。
部分名词解释:
MSL(Maximum Segment Lifetime)最长分节生命期 每个TCP实现必须选择一个MSL。它是任何报文段被丢弃前在网络内的最长时间。这个时间是有限的,因为TCP报文段以IP数据报在网络内传输,而IP数据报则有限制其生存时间的TTL时间。RFC 793指出MSL为2分钟,现实中常用30秒或1分钟。
参考:
《TCP+IP详解卷1:协议 原书第2版》
《Uinx网络编程》