TCP状态转换

 

       为了清楚的掌握连接建立,连接终止以及数据传送时发生的所有不同事件,下图以有限状态机的形式来定义。

TCP状态转换

 

 TCP各种状态

状态                               说明

CLOSED                      没有连接

LISTEN                         收到了被动打开;等待SYN

SYN-SENT                   已发送SYN;等待ACK

SYN-RCVD                   已发送SYN+ACK;等待ACK

ESTABLISHED             建立连接;数据传送在进行

FIN-WAIT-1                   第一个FIN已发送;等待ACK

FIN-WAIT-2                   对一个FIN的ACK已经收到,等待第二个ACK

CLOSE-WAIT               收到第一个FIN,已发送ACK;等待应用程序关闭

TIME-WAIT                   收到第二个FIN;已发送ACK;等待2MSL超时

LAST-ACK                    已发送第二个FIN;等待ACK

CLOSING                     双方都已决定同时关闭

 

连接建立和半关闭终止

       描述服务器进程发起被动打开和被动关闭,而客户端发起主动打开和主动关闭的情况。半关闭终止使我们能够展示更多的状态。

TCP状态转换

客户端状态

客户端进程向他的TCP发出一个命令,以请求连接到某个特定的套接字地址,这就称为主动打开。于是TCP发送一个SYN的报文段,并进入到SYN-SENT状态,在收到SYN-ACK报文之后,TCP发送ACK报文段,并进入ESTABLISHED状态。此后数据开始传送和确认,一般都是双向的。当客户进程没有更多的数据要传送时,就发出称为主动关闭的命令。于是客户端TCP发送FIN报文段,并进入FIN-WAIT-1状态。当它收到刚才发送FIN报文段的ACK后,就进入到FIN-WAIT-2状态,并继续停留在这个状态,直到收到服务端的FIN报文位置。在收到这个FIN报文段后,客户端就发送ACK报文段,并进入TIME-WAIT状态,同时设置一个计时器,它的超时时间是最大报文段寿命(Maximun Segment Lifetime,MSL)的两倍,MSL是一个报文段被丢弃之前在因特网中能够生存的最大时间。

       我们可以回想一下,TCP报文段是封装在生存时间(TTL)受限的IP数据报中。当IP数据报被丢弃时,封装在其中的TCP报文也就丢失了。MSL的常用数值是30-60秒。这里有两个理由使得我们需要TIME-WAIT状态和2MSL计数器。

 

通过时间线展示同样的过程。

TCP状态转换

1、如果最后一个ack报文段丢了,那么服务TCP(它为最后的FIN设置了计时器)以为是它的FIN丢失了,因而重传它。如果客户端已经进入CLOSED状态,并在2MSL计时器超时之前就关闭了这条连接,那么客户端就永远收不到这个重传的FIN报文段,因而服务器也就永远收不到最后的ACK。服务器无法关闭这条连接。2MSL计时器可以使客户端等待足够长的时间,使得在ACK丢失(1个MSL)的情况下,可以等到下一个FIN的到来(另一个MSL)。如果在TIME-WAIT状态中有一个新的FIN到达了,客户就发送一个新的ACK,并重新启动这个2MSL计时器。

2、某个连接中的重复报文段可能会出现在下一个连接中。假定客户和服务器已关闭了连接。经过短暂的时间后,他们又打开了一个新的连接,使用的是相同的套接字地址(同样的源地址和目的地址,同样的源端口号和目的端口号),这样的连接称为旧连接的化身。如果两个连接之前的时间间隔很短,那么前一个连接中的重复报文段有可能会到达新连接中,同时解释为属于这个新连接的报文段。为了避免这个问题,TCP规定这种化身必须经过2MSL时间之后才能出现。但是某些实现中,如果这个化身的初始序号大于钱一个连接使用的最后一个序号,就会忽略这个规则。

 

服务状态  

在我们假设的情况下,服务端进程发出一个打开命令。这必须在客户端发出打开命令之前发生。服务端TCP进入Listen状态,并继续被动的处于这个状态中,直至收到SYN报文为止。当服务器TCP收到SYN报文后,就发送SYN+ACK的报文段,并进入SYN+RCVD状态,等待客户端发送ACK报文段。在收到ACK报文段就进入到ESTABLISHED状态,此时可进行数据传送。虽然双方都可以发起关闭流程,但是从大多数情况的角度考虑,我们假设客户端发起关闭。

       TCP可以一直保持在这个状态,直至收到来自客户端TCP的FIN的报文段,这表示没有数据要发送了,因而连接可以关闭。此时,服务端向客户端发送ACK报文段,并发送队列中尚未发送完成的数据,然后进入CLOSE-WAIT状态。我们假设的情况时半关闭终止,服务TCP可以继续向客户端发送数据和接收确认,但没有反方向的数据能够传送过来。服务器TCP可以一直处于这个状态,知道应用程序真正的发出关闭命令。这是服务器TCP向客户端发送FIN报文标识它也要关闭这个链接了,并进入到LAST-ACK状态。服务器TCP一直处于这个状态直至收到最后的ACK报文段,然后进入CLOSED状态。从第一个FIN报文段开始到终止段称为四次握手【挥手】。

 

 

 

常见情况

       在连接建立和终止阶段更为常见的是使用三次握手,下图所示这种情况下客户和服务器的状态转换图。

TCP状态转换

 

下图用时间线描述了相同的情况。其中连接建立阶段与前一种情况中的相同,我们仅展示连接终止的阶段。

 TCP状态转换

        在数据传输完成后,客户进程发出关闭命令。客户TCP发送FIN报文,并进入FIN-WAIT-1状态。服务器TCP在收到这个FIN报文后,继续向客户端发送队列剩余的所有数据,并在最后附上EOF标记,表示这个连接要关闭了。服务器TCP进入CLOSE-WAIT状态,但它会推迟对收到的由客户发送来的FIN报文段的确认,直至它收到从自己的进程发送过来的被动关闭命令。在收到被动关闭命令后,服务器TCP就向客户发送FIN+ACK报文段,并进入LAST-ACK状态,等待最后的ACK。从中可以看出,客户取消了FIN-WAIT-2状态而直接进入TIME-WAIT状态。剩下的部分和四次挥手相同。

 

 

同时打开

       同时打开(simultaneous open)就是双方的应用程序都发出主动打开命令。这是一种非常还见的情况。此时没有客户,也没有服务器,通信的对方是对等的,彼此知道双方的本地端口号。TCP允许出现这种情况,但它很少会出现,因为双方都需要向对方发送SYN报文段,这两报文段要在同时传送。也就是说,两个应用程序必须几乎同时地发出主动打开命令,下图给出了这种情况下连接建立阶段。双方的TCP要经过SYN-SENT和SYN-RCVD状态之后才能进入ESTABLISHED状态。更仔细的观察可以看出这两个进程在同一时刻既充当了客户,又充当了服务。两个SYN-ACK报文段确认了两个SYN报文段后,连接就打开了。请注意,这个连接建立包括四次挥手。数据传送阶段和连接终止阶段和前面的例子一样。

TCP状态转换

 

 

同时关闭

      同时关闭(simultaneous close)在这种情况下,两端都主动关闭。双方的TCP都进入FIN-WAIT-1状态,并发送FIN报文段,这两个报文段同时在传送。在收到FIn报文段后,每一端都进入CLOSING状态,并发送ACK报文段。CLOSING状态取代了常见情境中FIN-WAIT-2和CLOSE-WAIT。在收到ACK后,双方都进入TIME-WAIT状态。请注意,这个时间段对双方来说都是需要的,因为双方都发送了ACK,而他们都有可能丢失。

TCP状态转换

 

 

拒绝连接

      另一种创建的情况时发生在服务TCP拒绝连接时,可能因为SYN报文段中目的端口定义的服务器当时并没有处于LISTEND状态。服务器TCP在收到这样的SYN报文段后会发送一个RST+ACK报文段,它确认了SYN报文段。但与此同时充值(拒绝)这条连接。服务器TCP进入LISTEN状态等待另一个连接。客户在收到这个RST+ACK报文段后进入CLOSED状态。

TCP状态转换

 

异常终止连接

TCP状态转换

       除了关闭连接,进程还可以异常终止连接。出现这种情况时因为进程出了故障(可以能是死锁在无限循环中),或者是不想发送队列中的数据了(由于数据中存在某些不一致)。另外,TCP也有可能想要异常终止一条连接。如果TCP收到了属于上一个连接的报文段(化身)就有可能发生这种情况。在所有这些情况下,TCP可以通过发送RST报文段使连接异常终止。图中的客户TCP发送RST+ACK报文段,并丢弃队列中的所有数据,服务TCP也把队列中的所有数据都丢弃,并通过差错报文来通知服务器进程。双方的TCP都立即进入CLOSED状态。请注意,对RST报文段不用相应ACK报文段。