TCP报文结构
在理解TCP的断开与连接过程之前,最好先了解一下TCP的报文结构,这样更有利于理解TCP的断开与连接过程
TCP报文结构如下:
在这里我们暂时只需重点关注:SYN、序号(sequence number)、确认号(Acknowledgment number)、FIN
TCP连接过程——三次握手
接下来,我们来观察TCP是如何建立一条连接的。
假设现在有一台主机(客户端)向另一台主机(服务器)发起一条TCP连接。客户端会用以下的方式向服务器发起一条TCP连接
第一次握手:客户端的TCP首先向服务器的TCP发送一个特殊TCP报文段。该TCP报文段不包含任何应用层数据,即TCP报文中数据
字段没有认识数据,但报文段首部的SYN
标志位会被置为1,这个特殊的报文段我们成为SYN报文段
。同时随机产生一个初始序号(client_isn
,一个指代而已,称作x也是可以的),并将该序号放在首部的序号(sequence number)
字段中。客户端发送完SYN报文段后,客户端进入SYN_WAIT
状态,等待服务器确认。
第二次握手:服务器收到客户端发来的SYN报文段后,会为该TCP连接分配缓存和变量,同时向客户端发送一个允许连接的报文段,该报文段中也不包含应用层数据,它的SYN
字段被置为1,确认号字段被置为client_isn+1
,同时选择一个随机的初始化序号(server_isn)
放入报文段的序号(sequence number)
字段中。这个允许连接的报文段一般被称为SYNACK报文段
。此时服务器进入SYN_RECV状态
第三次握手: 客户端收到SYNACK报文段后,也要为该TCP连接分配缓存变量。客户端要向服务端发送另一个报文段,该报文段将SYN置为1,序号字段置为client_isn + 1
,确认号字段被置为server_isn+1
,这次的报文可以携带应用层的数据。此时客户端进入连接建立(ESTABLISHED)
状态。服务端收到这个报文段后,也进入连接建立(ESTABLISHED)
状态,此时连接就算完全建立好了,双方可以相互发送数据
TCP断开过程——四次挥手
既然有连接,那肯定有断开。不过首先要明确一点,TCP的断开既可以由客户端发起也可以有服务端发起,这里主要讨论客户端发起断开的过程
TCP断开过程如下:
(1):客户端发送一个关闭连接的报文段给服务器,该报文段是个特殊的报文段,它的FIN标志位被置为1。发送完报文段后,客户端从ESTABLISHED
转为FIN_WAIT_1
状态,等待服务器的报文响应
(2):服务器收到终止连接的报文段后,发送一个确认报文段给客户端,表示收到,通知客户端可以释放连接了。当客户端收到这个ACK报文时,单方面释放了与服务器的连接,此时他不能再向服务器发送应用层数据,但由于服务器可能还有数据没有发送完,所以此时客户端还可以接收来自服务器的报文段。此时客户端进入FIN_WAIT_2
状态,等待来自服务器的FIN报文。服务器由ESTABLISHED
状态进入CLOSE_WAIT
状态。
(3):当服务器向客户端发送完所有数据后,服务器发送它自己的终极报文段,其FIN标志位也被置为1,并等待客户端的确认,此时服务器进入LAST_ACK
状态。
(4):客户端收到服务器关闭连接的报文段后,发送一个确认报文段,通知服务器可以释放连接资源了,服务器收到客户端的确认报文段后,释放所有连接资源,进入CLOSED
状态。而A并没有马上释放资源,而是进入TIME_WAIT
状态,等待一段时间(自定义,通常为30sec、1min或2min),使得可以在确认报文段丢失的情况下进行重传。在等待时间结束之后,连接正式关闭,客户端的所有资源也被释放,进入CLOSED
状态
总结
结合TCP的三次握手和四次挥手,可以等到如下的状态图
最后说一句:TCP的断开之所以需要进行四次挥手,在于某一方发起关闭连接的请求时,可以保证自己不再需要发送数据,但并不能猜测到对方是否已经发送完数据了,所以只是先通知对方我要断开连接了,你也差不多可以断开了,对方可以断开的时候则又会发一个断开连接的报文段。而额外的两个的ACK报文是确保两个FIN报文都被接收到