网络基础问题整理:为什么TCP四次挥手最后需要TIME_WAIT状态?

一:首先奉上
TCP三次握手的过程
TCP三次握手的过程
TCP连接释放的过程:
TCP连接释放的过程
二:
1。为什么两次就建立连接还要三次握手呢?这主要是为了防止已失效的连接请求报文又突然传递服务器。
所谓“防止已失效的连接请求报文又突然传递服务器。”是这样一种情况:
A客户端发出连接请求,因为连接请求报文丢失而未等到确认。于是A再次重传了连接请求,建立了连接。数据传输完毕后,释放了连接。现在假设那第一个请求只是因为网路节点长时间滞留了,使得它在第二个连接释放后才到达B服务器,那么B会以为这是一个新的连接请求,于是就向A发了个连接确认,注意了:如果没有最后一次的确认B会一厢情愿的以为连接已经建立,可人家A同学一看那个B给的是什么呀!跟自己没关系,简单粗暴的丢掉。这时B孩子还傻傻的等着A给他发数据,就这样,B白白浪费的大把的时光和资源。
那B会一直傻等吗?当然不是,它的等待也是有限的,答案就是保活计时器。

2.为什么要有四次挥手的TIME_WAIT的状态?
(1)第一是为了保证最后一个的一个ACK报文能到达B。这个ACK报文有可能丢失,因而使得处在LAST_ACK状态得不到对已发送的FIN+ACK报文的确认,B会超时重传这个FIN+ACk ,而A就能在这TIME_WAIT时间(2MSL)里收到这个重传的报文,A就可以重传一次确认,如果没有这个TIME_WAIT, 那B重传的FIN_ACK,可A早就走了,自然不会再重发确认,这样B就无法按照正常步骤进入CLOSE 状态。
(2)第二是防止“已失效的报文连接请求”,A在TIME_WAIT中,经过这2MSL的时间,就可以使本链接持续的时间内产生的所有连接消失,这样就可以使下一个新的连接中不会出现这样旧的连接请求报文段。
2.2 聪明的你会发现谁先关闭谁就有一个TIME_WAIT的状态;
在linux的网络编程中,如果服务器如果先关闭,你会发现,现在想要立马再次启动服务器,就会报错说这个端口号被占用着,那就是因为有这个TIME_WAIT,2msl的时间.那么怎么解决 ?
解决:setsockopt()函数。在这就不多说了。

根据第三版《UNIX网络编程 卷1》2.7节,TIME_WAIT状态的主要目的有两个:

优雅的关闭TCP连接,也就是尽量保证被动关闭的一端收到它自己发出去的FIN报文的ACK确认报文;

处理延迟的重复报文,这主要是为了避免前后两个使用相同四元组的连接中的前一个连接的报文干扰后一个连接。

很明显,要实现上述两个目标,TIME_WAIT状态需要持续一段时间,但这段时间应该是多长呢?

如果只考虑上述第一个目标,则TIME_WAIT状态需要持续的时间应该参考对端的RTO(重传超时时间)以及MSL(报文在网络中的最大生存时间)来计算而不是仅仅按MSL来计算,因为只要对端没有收到针对FIN报文的ACK,就会一直持续重传FIN报文直到重传超时,所以最能实现完美关闭连接的时长计算方式应该是从对端发送第一个FIN报文开始计时到它最后一次重传FIN报文这段时长加上MSL,但这个计算方式过于保守,只有在所有的ACK报文都丢失的情况下才需要这么长的时间;另外,第一个目标虽然重要,但并不十分关键,因为既然已经到了关闭连接的最后一步,说明在这个TCP连接上的所有用户数据已经完成可靠传输,所以要不要完美的关闭这个连接其实已经不是那么关键了。因此,(我猜)RFC标准的制定者才决定以网络丢包不太严重为前提条件,然后根据第二个目标来计算TIME_WAIT状态应该持续的时长。

对于刚才说的第二点,如何理解TIME_WAIT状态持续2MSL的时间就可以避免前后两个使用相同四元组的连接中的前一个连接的报文干扰后一个连接呢?

首先我们需要了解如下要点:

TCP连接中的一端发送了FIN报文之后如果收不到对端针对该FIN的ACK,则会反复多次重传FIN报文,大约持续几分钟;

被动关闭处于LAST_ACK状态的一端在收到最后一个ACK之后不会发送任何报文,立即进入CLOSED状态;

主动关闭的一端在收到被动关闭端发送过来的FIN报文并回复ACK之后进入TIME_WAIT状态;

之所以TIME_WAIT状态需要维持一段时间而不是进入CLOSED状态,是因为需要处理对端可能重传的FIN报文或其它一些因网络原因而延迟的数据报文,不处理这些报文可能导致前后两个使用相同四元组的连接中的后一个连接出现异常(详见UNIX网络编程卷1的2.7节 第三版);

处于TIME_WAIT状态的一端在收到重传的FIN时会重新计时(rfc793 以及 linux kernel源代码tcp_timewait_state_process函数)。

下面我们开始分析为什么在发送了最后一个ACK报文之后需要等待2MSL时长来确保没有任何属于当前连接的报文还存活于网络之中(前提是在这2MSL时间内不再收到对方的FIN报文,但即使收到了对端的FIN报文也并不影响我们的讨论,因为如果收到FIN则会回复ACK并重新计时)。

为了便于描述,我们设想有一个处于拆链过程中的TCP连接,这个连接的两端分别是A和B,其中A是主动关闭连接的一端,因为刚刚向对端发送了针对对端发送过来的FIN报文的ACK,此时正处于TIME_WAIT状态;而B是被动关闭的一端,此时正处于LAST_ACK状态,在收到最后一个ACK之前它会一直重传FIN报文直至超时。随着时间的流逝,A发送给B的ACK报文将会有两种结局:

ACK报文在网络中丢失;如前所述,这种情况我们不需要考虑,因为除非多次重传失败,否则AB两端的状态不会发生变化直至某一个ACK不再丢失。

ACK报文被B接收到。我们假设A发送了ACK报文后过了一段时间t之后B才收到该ACK,则有 0 < t <= MSL。因为A并不知道它发送出去的ACK要多久对方才能收到,所以A至少要维持MSL时长的TIME_WAIT状态才能保证它的ACK从网络中消失。同时处于LAST_ACK状态的B因为收到了ACK,所以它直接就进入了CLOSED状态,而不会向网络发送任何报文。所以晃眼一看,A只需要等待1个MSL就够了,但仔细想一下其实1个MSL是不行的,因为在B收到ACK前的一刹那,B可能因为没收到ACK而重传了一个FIN报文,这个FIN报文要从网络中消失最多还需要一个MSL时长,所以A还需要多等一个MSL。

综上所述,TIME_WAIT至少需要持续2MSL时长,这2个MSL中的第一个MSL是为了等自己发出去的最后一个ACK从网络中消失,而第二MSL是为了等在对端收到ACK之前的一刹那可能重传的FIN报文从网络中消失。另外,虽然说维持TIME_WAIT状态一段时间有2个目的,但这段时间具体应该多长主要是为了达成上述第二个目的而设计的。

转自:
https://blog.csdn.net/lthirdonel/article/details/103015627
https://www.cnblogs.com/joker1937/p/12487776.html

猜你喜欢

转载自blog.csdn.net/boiled_water123/article/details/111399672