TIME_WAIT状态在tcp连接中的含义与作用

在tcp的socket通信中,主动关闭的Socket端会进入TIME_WAIT状态,并且持续2MSL时间长度。MSL就是maximum segment lifetime(最大分节生命期),这是一个IP数据包能在互联网上生存的最长时间,超过这个时间将在网络中消失。MSL在RFC 1122上建议是2分钟,而源自berkeley的TCP实现传统上使用30秒,因而,TIME_WAIT状态一般维持在1-4分钟。

TIME_WAIT状态存在的理由

1)可靠地实现TCP全双工连接的终止

在进行关闭连接四路握手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,服务器将重发最终的FIN,因此客户端必须维护状态信息允许它重发最终的ACK。如果不维持这个状态信息,那么客户端将响应RST分节,服务器将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。因而,要实现TCP全双工连接的正常终止,必须处理终止序列四个分节中任何一个分节的丢失情况,主动关闭的客户端必须维持状态信息进入TIME_WAIT状态。

2)允许老的重复分节在网络中消逝

TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个原来的迷途分节就称为lost duplicate。

在关闭一个TCP连接后,马上又重新建立起一个相同的IP地址和端口之间的TCP连接,后一个连接被称为前一个连接的化身(incarnation),那么有可能出现这种情况:前一个连接的迷途重复分组在前一个连接终止后出现,从而被误解成从属于新的化身。

为了避免这个情况,TCP不允许处于TIME_WAIT状态的连接启动一个新的化身,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个TCP连接的时候,来自连接先前化身的重复分组已经在网络中消逝。

新的SCTP协议通过在消息头部添加验证标志避免了TIME_WAIT状态。

 TIME_WAIT状态主要有两个作用

第一:确保主动发起关闭一方响应最后的一个ACK能够正常送达
第二:网络上仍然有可能有残余的数据包(wandering duplicates,或老的重复数据包),我们也必须能够正常处理。
见图解。

第一种情况好理解。保留连接状态2MSL(大约1-4分钟)长时间,确保4次握手最后一次ok。

第二种情况分析

如果 TIME_WAIT 状态保持时间不足够长(比如小于2MSL),第一个连接就正常终止了。

第二个拥有相同相关五元组的连接出现,而第一个连接的重复报文到达,干扰了第二个连接。TCP实现必须防止某个连接的重复报文在连接终止后出现,所以让TIME_WAIT状态保持时间足够长(2MSL),连接相应方向上的TCP报文要么完全响应完毕,要么被丢弃。建立第二个连接的时候,不会混淆。

编程中会经常使用SO_REUSEADDR,来表示可重用处于TIME_WAIT状态的相同五元组的连接。

由于TIME_WAIT状态所带来的相关问题,我们可以通过设置 SO_LINGER标志来避免socket进入TIME_WAIT状态,这可以通过发送RST而取代正常的TCP四次握手的终止方式。但这并不是一个很好的主意,TIME_WAIT对于我们来说往往是有利的。

一个TCP连接开始到结束的过程

Server                       Client
-------------- FIN -------------->      server: fin_wait_1
<------------- ACK ---------------      client: close_wait  server:fin_wait_2
<------------- FIN ---------------      client发出fin之后就关闭 
-------------- ACK ------------->       server发出ack后进入time_wait状态

Time_Wait的默认时间是2倍的MSL,就是240秒钟。MSL(maximum segment lifetime)是TCP 片在网上的最长存活时间。

TIME_Wait的主要作用是保证关闭的TCP端口不立即被使用。因为当网络存在延迟时,可能当某个端口被关闭后,网络中还有一些重传的TCP片在发向这个端口,如果这个端口立即建立新的TCP连接,则可能会有影响。所以使用2倍的MSL时间来限制这个端口立即被使用。这个等待时间基本是从1981年的RFC793过来的,对于服务器实在太长了些。而且每个TCP连接都各自有个数据结构,叫TCP Control block,Time_wait的时候这个数据结构没有被释放。所以当有太多的TCP连接时,内存可能会被占用很多。

如果有恶意程序大量的连接服务器,虽然很快断开,但产生大量的TIME_WAIT,逐渐消耗内存。虽然可以通过setsockopt(nListenSocket, SOL_SOCKET, SO_REUSEADDR, &nRet, n);让TIME_WAIT的本地地址和端口被新的进程使用,但系统给新进程分配的端口貌似只有一小部分落到了TIME_WAIT端口,还是无法解决TIME_WAIT带来的问题。

目前好像只能在TIME_WAIT的时间限制上做点文章了,不少管理员同志设置成30秒,至少能缓解服务器在高连接情况下的很大压力。

点赞 (0)

发表评论

电子邮件地址不会被公开。 必填项已用*标注