Time-wait状态(2MSL)一些理解

1. 编写TCP/SOCK 服务时,SO_REUSEADDR到底是什么意思?

这个套接字选项通知内核,如果端口忙,但TCP状态处于TIME_WAIT,可以重用端口。如果端口忙,TCP状态处于其他状态,重用端口时依旧指明“地址已经在使用中”。如果你的服务程序停止后向立刻重启,而新套接字依旧使用同一个端口,此时SO_REUSEADDR选项非常有用。但是必须意识到,此时任何非期望数据到达,都可能导致服务程序反应混乱。

一个套接字由五个部分组成:协议,本地地址,本地端口,远程地址和远程端口。SO_REUSEADDR仅仅表示可以重用本地地址,本地端口。

 

2. 为什么需要TIME_WAIT状态?

假设最后的ACK丢失,server将重发FIN,client必须维护TCP状态信息以便可以重发最后的ACK,否则将会发送RST,结果server认为发生错误。TCP实现必须可靠地终止连接的两个方向,所以client必须进入TIME_WAIT状态。

此外,考虑一种情况,TCP实现可能面临着先后两个相同的五元组。如果前一个连接处于TIME_WAIT状态,而允许另一个拥有相同五元组连接出现,可能处理TCP报文时,两个连接互相干扰。所以使用SO_REUSEADDR选项就需要考虑这种情况。

 

3. 什么是2MSL

MSL是Maximum Segment Lifetime,译为“报文最大生存时间”,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
因为TCP报文(segment)是IP数据报(datagram)的数据部分,而IP头中有一个TTL域,TTL是time to live的缩写,中文可以译为“生存时间”,这个生存时间是由源主机设置初始值但不是存的具体时间,而是存储了一个IP数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减1,当此值为0则数据报将被丢弃,同时发送ICMP报文通知源主机。
RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等
2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态,当TCP的一端发起主动关闭,在发出最后一个ACK包后,即第3次握手完成后发送了第四次握手的ACK包后就进入了TIME_WAIT状态,必须在此状态上停留两倍的MSL时间。
等待2MSL时间主要目的是怕最后一个ACK包对方没收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包。
在TIME_WAIT状态时两端的端口不能使用,要等到2MSL时间结束才可继续使用。
当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。不过在实际应用中可以通过设置SO_REUSEADDR选项达到不必等待2MSL时间结束再使用此端口。

TTL与MSL是有关系的但不是简单的相等的关系,MSL要大于等于TTL。

 

4. 为什么TIME_WAIT状态需要保持2MSL这么长的时间?

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

 

2MSL等待状态
TIME_WAIT状态也称为2MSL等待状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifetime)。它是任何报文段被丢弃前在网络内的最长时间。我们知道这个时间是有限的,因为TCP报文段以IP数据报在网络内传输,而IP数据报则有限制其生存时间的TTL字段。
对一个具体实现所给定的MSL值,处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样可让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)。
这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的套接口(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用。这个连接只能在2MSL结束后才能再被使用。遗憾的是,大多数TCP实现(如伯克利版)强加了更为严格的限制。在2MSL等待期间,套接口中使用的本地端口在默认情况下不能再被使用。
在连接处于2MSL等待时,任何迟到的报文段将被丢弃。因为处于2MSL等待的、由该套接口对(socket pair)定义的连接在这段时间内不能被再用,因此当要建立一个有效的连接时,来自该连接的一个较早替身( incarnation)的迟到报文段作为新连接的一部分不可能不被曲解(一个连接由一个插口对来定义。一个连接的新的实例( instance)称为该连接的替身)。
客户执行主动关闭并进入TIME_WAIT是正常的。服务器通常执行被动关闭,不会进入TIME_WAIT状态。这暗示如果我们终止一个客户程序,并立即重新启动这个客户程序,则这个新客户程序将不能重用相同的本地端口。这不会带来什么问题,因为客户使用本地端口,而并不关心这个端口号是什么。
然而,对于服务器,情况就有所不同,因为服务器使用熟知端口。如果我们终止一个已经建立连接的服务器程序,并试图立即重新启动这个服务器程序,服务器程序将不能把它的这个熟知端口赋值给它的端点,因为那个端口是处于2MSL连接的一部分。在重新启动服务器程序前,它需要在1~4分钟。
尽管许多具体的实现中允许一个进程重新使用仍处于2MSL等待的端口(通常是设置选项SO _REUSEADDR),但TCP不能允许一个新的连接建立在相同的插口对上。

平静时间的概念
对于来自某个连接的较早替身的迟到报文段, 2MSL等待可防止将它解释成使用相同插口对的新连接的一部分。但这只有在处于2MSL等待连接中的主机处于正常工作状态时才有效。如果使用处于2MSL等待端口的主机出现故障,它会在MSL秒内重新启动,并立即使用故障前仍处于2MSL的插口对来建立一个新的连接吗?如果是这样,在故障前从这个连接发出而迟到的报文段会被错误地当作属于重启后新连接的报文段。无论如何选择重启后新连接的初始序号,都会发生这种情况。
为了防止这种情况,RFC 793指出TCP在重启动后的MSL秒内不能建立任何连接。这就称为平静时间(quiet time)。只有极少的实现版遵守这一原则,因为大多数主机重启动的时间都比MSL秒要长。

posted on 2020-10-09 18:54  Code2020  阅读(2020)  评论(0编辑  收藏  举报