TCP连接中的TIME_WAIT状态二
一、TIME_WAIT状态的必要性
1、 客户端发送FIN报文段,进入FIN_WAIT_1状态。
2、 服务器端收到FIN报文段,发送ACK表示确认,进入CLOSE_WAIT状态。
3、 客户端收到FIN的确认报文段,进入FIN_WAIT_2状态。
4、 服务器端发送FIN报文端,进入LAST_ACK状态。
5、 客户端收到FIN报文端,发送FIN的ACK,同时进入TIME_WAIT状态,启动TIME_WAIT定时器,超时时间设为2MSL。
6、 服务器端收到FIN的ACK,进入CLOSED状态。
7、 客户端在2MSL时间内没收到对端的任何响应,TIME_WAIT超时,进入CLOSED状态。
尽最大努力保证四次握手正常关闭
如果不考虑报文延迟、丢失,确认延迟、丢失等情况,TIME_WAIT的确没有存在的必要。当网络在不理想的情况下通常会有报文的丢失延迟发生,让我们看下面的一个特例:
客户端进入发送收到四次握手关闭的最后一个ACK后,进入TIME_WAIT同时发送ACK,如果其不停留2MSL时间,而是马上关闭连接,销毁连接上的资源,当发送如下情况时,将不能正常的完成四次握手关闭:
客户端发送的ACK在网路上丢失,这样服务器端收不到最后的ACK,重传定时器超时,将重传FIN到客户端,由于客户端关于该连接的所有资源都释放,收到重传的FIN后,它没有关于这个FIN的任何信息,所以向服务器端发送一个RST报文端,服务器端收到RST后,认为搞连接出现了异常(而非正常关闭)。
所以,在TIME_WAIT状态下等待2MSL时间端,是为了能够正确处理第一个ACK(最长生存时间为MSL)丢失的情况下,能够收到对端重传的FIN(最长生存时间为MSL),然后重传ACK。
是否只要主动关闭方在TIME_WAIT状态下停留2MSL,四次握手关闭就一定正常完成呢?
答案是否定的?可以考虑如下的情况,
结论:在TIME_WAIT下等待2MSL,只是为了尽最大努力保证四次握手正常关闭。
确保老的报文段在网络中消失,不会影响新建立的连接
考虑如下的情况,主动关闭方在TIME_WAIT状态下发送的ACK由于网络延迟的原因没有按时到底(但并没有超过MSL的时间),导致被动关闭方重传FIN,在FIN重传后,延迟的ACK到达,被动关闭方进入CLOSED状态,如果转动关闭方在TIME_WAIT状态下发送ACK后马上进入CLOSED状态(也就是没有等待)2MSL时间,则上述的连接已不存在:现在考虑下面的情况,假设上述的链接的四元组为(192.201.0.80:23,192.201.0.85:5555),由于连接已关闭,我们可以马上建立一个新的连接,并且这个连接的四元组也是(192.201.0.80:23,192.201.0.85:5555),那么当上一个连接的重传FIN到达主动关闭方时,被新的连接所接受,这将导致新的连接被复位,很显然,这不是我们希望看到的事情。
新的连接要建立,必须是在主动关闭方和被动关闭方都进入到CLOSED状态之后才有可能。所以,最有可能导致旧的报文段影响新的连接的情况是:
在TIME_WAIT状态之前,主动关闭方发送的报文端在网络中延迟,但是TIME_WAIT设定为2MSL时,这些报文端必然会在网络中消失(最大生存时间为MSL)。被动关闭方最有可能影响新连接的报文段就是我们上面讨论的情况,对方ACK延迟到达,在此之前重传的FIN,这个报文端发送之后,TIME_WAIT的定时器超时时间肯定大于MSL,在1MSL时间内,这个FIN要么在网络中因为生成时间到达而消失,要么到达主动关闭方被这确的处理,不会影响新建立的连接。
二、 平静时间
对于来自某个连接的较早替身的迟到报文段,2MSL等待可以防止将它解释成为使用相同插口对的新的连接的一部分。但这只有在2MSL等待连接中的主机处于正常工作状态时才有效。
如果使用处于2MSL等待端口的主机出现故障,它会在MSL秒内重新启动,并立即使用故障前仍出入2MSL的插口对来建立一个新的连接吗?如果是这样,在故障前从这个连接发出而迟到的报文段会被错误地当作属于重启后新连接的报文端,无论如何重新选择重启后新的连接的初始序号,都会发生这种情况。
为了防止这种情况,RFC 793指出TCP在重启后的MSL秒内不能建立任何连接。这就是平静时间(quit time)。(有疑问???)
三、TIME_WAIT、LAST_ACK状态对收到的报文段的处理
1. TIME_WAIT收到报文的处理
1)、收到SYN报文段
if (tiflags & TH_SYN &&
tp->t_state == TCPS_TIME_WAIT &&
SEQ_GT(ti->ti_seq, tp->rcv_nxt)) {
iss = tp->rcv_nxt + TCP_ISSINCR;
tp = tcp_close(tp);
goto findpcb;
}
在TIME_WAIT状态,如果收到SYN报文段,且新的起始序号大于连接上最后收到的序号,说明对端要求在已被关闭且正处于TIME_WAIT状态的连接上重新连接。RFC 1122允许这种情况:调用TCP_CLOSE释放处于TIME_WAIT状态的原有连接的PCB和TCP控制块。控制跳转到findpcb,寻找监听服务器,为新的连接创建新的插口。
2) 、收到RST报文段
if (tiflags&TH_RST)
switch (tp->t_state) {
case TCPS_SYN_RECEIVED:
so->so_error = ECONNREFUSED;
goto close;
case TCPS_ESTABLISHED:
case TCPS_FIN_WAIT_1:
case TCPS_FIN_WAIT_2:
case TCPS_CLOSE_WAIT:
so->so_error = ECONNRESET;
close:
tp->t_state = TCPS_CLOSED;
tcpstat.tcps_drops++;
tp = tcp_close(tp);
goto drop;
case TCPS_CLOSING:
case TCPS_LAST_ACK:
case TCPS_TIME_WAIT:
tp = tcp_close(tp);
goto drop;
}
如果在TIME_WAIT状态收到RST报文段,这释放连接资源,跳转到CLOSED状态,提前终止TIME_WAIT状态。
如果允许RST终止处于TIME_WAIT状态的连接,那么TIME_WAIT状态也就没有存在的必要。RFC 1337讨论了这一点,及其他取消TIME_WAIT状态可能的情况,建议不允许RST永久终止处于TIME_WAIT状态的连接。
3) 收到ACK报文段
此时,连接的两端已发送过FIN,且两个FIN都已被确认。但如果TCP对远端FIN的确认丢失,对端将重传FIN(带有ACK)。TCP丢弃报文端并重传ACK。此外TIME_WAIT定时器必须被重置,时限等于2MSL。
4)收到FIN报文段
如果在TIME_WAIT状态收到FIN,说明这是一个重复报文端。启动TIME_WAIT定时器,时限等于2MSL,发送ACK。
2、LAST_ACK状态
1)收到RST报文段
如果在TIME_WAIT状态收到RST报文段,则释放连接资源,跳转到CLOSED状态。
2)收到ACK置位的报文段
case TCPS_LAST_ACK:
if (ourfinisacked) {
tp = tcp_close(tp);
goto drop;
}
break;
如果FIN已确认,连接转移到CLOSED状态。tcp_close负责将这一状态变迁,并同时释放Interenet PCB和TCP控制块。