TIME-WAIT状态的主要做用在于TCP链接的拆除阶段。拆除一个TCP链接一般须要交换4个segment,以下图所示:程序员
Host1上的应用程序关闭了本身这一端的链接,使得TCP向Host 2发送了一个FIN。Host 2对这个FIN进行ACK,并将这个FIN做为一个EOF传送给应用程序(假设Host 2上的应用程序有一个挂起的read操做)。通过一段时间后,Host 2上的应用程序close掉本身那一端的链接,这样TCP会向Host 2发送一个FIN,Host 1会以一个ACK应答。服务器
此时,Host 2关闭链接,并释放资源。从Host 2的角度来看,链接已经不复存在了。可是,Host 1尚未关闭链接,而是会进入TIME-WAIT状态,并在这个状态停留2MSL(2 Maximum Segment Lifetime)。网络
MSL(最大分段寿命)是segment被丢弃以前在网络中能够存活的最大时长。socket
等待了2MSL以后,Host 1也将TCP链接关闭,并释放资源。ide
注意blog
关于TIME-WAIT状态,须要注意如下3点:资源
1. 一般,只有主动关闭TCP链接的那一端会进入TIME-WAIT状态。get
主动关闭表示发送第一条FIN的那一端。另外一端称为被动关闭。有一种多是同时关闭,即两端同时关闭链接,并向网络发送FIN,在这种状况下,就认为两个应用程序都进行了主动关闭,两端都会进入TIME-WAIT状态。it
2. RFC将MSL定义为2分钟,根据这个定义,链接会在TIME-WAIT状态停留4分钟。但实际上,各实现均有不一样,通常TIME-WAIT状态持续时间在1分钟到4分钟之间。io
3. 若是链接处于TIME-WAIT状态时有segment到达,就重启2MSL的定时器。
使用TIME-WAIT状态主要有两个目的:
1. 维护链接状态
It maintains the connection state in case the final ACK, which is sent by the side doing the active close, is lost, causing the other side to resend its FIN.
若是主动关闭链接的那端发送的最后一条ACK丢失,那么另外一端会从新发送FIN(注意,这二者一来一回最长的时间正好是2MSL),若是此时主动关闭方已经关闭了链接的话(也就是说没有TIME-WAIT的话),那么主动方收到对方重送的FIN后,因为TCP已经没有这条链接的记录了,会发送RST(重置)给对端,对端会将此标记为error,于是没法正常关闭链接。可是,若是主动方处于TIME-WAIT状态,那么它就仍然有这条TCP链接的记录,就能够对对端重送的FIN进行正确的相应。
这也就解释了为何链接在TIME-WAIT状态收到一个segment时,须要重启2MSL的定时器。若是最后一条ACK丢失,对端重传FIN,处于TIME-WAIT状态的这一端会再次对FIN进行ACK,而且重启定时器以防这一条ACK也丢失。
2. 为了耗尽网络中全部基于此链接的“走失的segment”提供时间。
It provides time for any ‘stray segments’ from the connection to drain from the network.
IP数据报在网络上传输时,不免会发生丢失或延迟,固然,TCP会有一些机制来保证对没有被即时ACK的segment进行重传。
若是没有TIME-WAIT状态,而且,这些延迟的或者被重传的segment在链接被关闭后到达,TCP只是将这些segment丢弃并以RST回应,当RST到达另外一端时,因为这台Host中也没有这条TCP链接的记录了,因此RST会被丢弃,在这种状况下不会产生问题。可是,若是在这两台主机上用一样的端口号创建了一条新链接,那么以前那个TCP链接中延迟的或者被重传的segment的sequence number可能正好落在新链接的接收窗口中(receive sliding window),那么这部分的数据就会被接收,从而对新链接形成破坏。
TIME-WAIT状态确保了在原有链接的全部segment从网络中消失以前,不会再次使用原来用过的socket对(两个IP地址以及相应的端口号),所以TIME-WAIT状态在提供TCP可靠性方面发挥了重要的做用。
不幸的是,TIME-WAIT状态能够被提早终止,这被称为TIME-WAIT暗杀。有如下两种状况:
1. RFC 793指出,当一条链接处于TIME-WAIT状态并收到一个RST时,应该当即将链接关闭。
假设,如今有一条链接处于TIME-WAIT状态,而且以前一个延迟的或者被retransmission的segment到达,而这个segmentTCP没法接收(例如序列号在接受窗口以外),TCP会以一个ACK相应,说明它所期待的序列号(就是对等实体的FIN以后的序列号)。但对等实体中已经没有这个TCP链接的记录了,因此会以一个RST来进行ACK。当这个RST回处处于TIME-WAIT状态的那一端时,会使链接当即关闭——TIME-WAIT状态被暗杀了。
幸运的是,对TCP进行修改,使其忽略TIME-WAIT状态下的RST便可,某些协议栈进行了这样的修改。
2. 另外一种TIME-WAIT暗杀的方式是有意为之的。
即便应用程序是TCP链接的主动关闭方,程序员也可使用套接字选项 SO_LINGER 迫使链接当即关闭,而不通过TIME-WAIT状态。有时,会推荐使用这种可疑的方式使得服务器跳出TIME-WAIT状态,这样就能够在服务器重启或者崩溃后快速重启。可是,健壮的应用程序永远都不该该干扰TIME-WAIT状态,由于这是TCP可靠机制的重要组成部分。
一般,应用程序关闭一条链接时,即便TCP发送缓冲区中仍然有数据要发送,close调用也会当即返回,虽然,TCP仍是会尝试着发送未发送出去的数据,可是应用程序并不知道是否发送成功了。为了防止这个问题的发生,能够设置套接字选项SO_LINGER(经过填写linger结构体,并调用setsockopt来设置套接字的SO_LINGER选项)。
若是成员l_onoff为零,那么linger选项将被关闭,其行为与默认行为相同——close调用当即返回,内核继续尝试发送全部未发送的数据。
若是l_onoff非零,其行为取决于l_linger的值。若是l_linger非零,就将l_linger做为内核等待挂起的数据被发送出去并被确认(ACK)而逗留的时间间隔。也就是说,close在全部数据都发送完毕,或者时间间隔到期以前是不会返回的。
若是l_linger时间到了,仍然有未发送的数据,close就返回EWOULDBLOCK,全部未发送的数据均可能丢失。若是数据都发送出去了,close就返回0。
固然,以这种方式使用SO_LINGER,能保证的也只是数据到达了对方的TCP接收缓冲区中,不能保证数据被对面的应用程序读走。
最后若是l_onoff非零,l_linger为零,TCP链接会被丢弃。也就是说,向对等实体发送一个RST(代表链接已经不存在),不通过TIME-WAIT状态就当即关闭链接。这就是所谓的有意的TIME-WAIT暗杀。