TCP是面向连接的协议,运输层依靠tcp协议来传输报文。TCP运输连接的建立和释放是每一次面向连接的通信比不可少的过程。运输过程包括三个阶段,即连接建立、数据传送、连接释放。

TCP报文段的首部格式
由于tcp连接时传输的是报文段,在了解运输过程前先了解下tcp报文段的格式,理解了各字段的意义,对于三次握手和四次挥手的理解很有帮助。
-
源端口和目的端口: 各占2个字节,分别为源端口号和目的端口号;
-
序号:占4字节;序号范围是[0,2^32-1] ,当序号增加到2^32-1后,下一个就又回到0。在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。首部的序号字段表示本报文段所发送的数据第一个字节的序号。例如当一个报文段的序号字段值为301,携带的数据共有100字节,那么下一个报文段的数据序号应当从401开始。
-
确认号:占4字节;是期望收到对方下一个报文段的第一个数据字段的序号。例如服务B收到了A发送的报文段,其首部序号为501,数据长度是200字节,这表明B收到了A发送的到序号700为止的数据。因此B期望下一次收到A的报文序号为701,所以会返回确认号701给A。即确认号(ack)= 收到的序号 + (数据长度) + 1;
注意:在建立TCP连接时是不带数据的,所以确认号刚好等于收到的序号+1;
-
数据偏移: 占4位。它指出TCP报文段的数据起始处距离TCP报文段起始处有多远。
-
保留:占6位,保留今后使用,但目前置为0;
-
紧急URG: 当URG=1,表示紧急指针字段有效,它会告诉系统报文段中有紧急数据,应优先处理。例如用户键盘的Control+C终止命令。
-
确认号ACK: 与上述确认号不同,该确认号只有0或1;当置为1的时候,确认字段才有效;TCp规定,在连接建立后所有传送的报文段必须把ACK置为1;
-
推送PSH: push标志,为1表示是带有push标志的数据,指示接收方在接收到该报文段以后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队。
-
复位RST:重置连接标志,用于重置由于主机崩溃或其他原因而出现错误的连接。或者用于拒绝非法的报文段和拒绝连接请求。当RST=1时,表明TCP连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接;
-
同步SYN: 在连接建立时用来同步序号。当SYN=1,而ACK=0时,表示这是一个连接请求报文段。当SYN=1,ACK=1时,表示这是一个连接接收报文段。因此,SYN=1就表示这是一个连接请求或连接接受报文;
-
终止FIN: 用来释放一个连接。当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。
-
窗口:占2字节。窗口指的是发送本报文段的一方的接受窗口(而不是自己的发送窗口)。窗口值作为接收方让发送方设置其发送窗口的数据;
TCP的三次挥手
TCP的连接建立,即TCP的三次挥手。连接建立前,最初两端的TCP进程都处于CLOSED(关闭)状态;
-
连接建立时,客户端A会先发送连接请求到服务端B,发送序号seq=x,将SYN同步位置为1,A进入SYN-SENY(即同步已发送)状态;
-
B收到连接请求报文后,如同意连接,即向A发起确认;将发送序号seq置为y,同步位SYN置为1,确认序号ACK也置为1,同时将确认号ack置为x+1。此时B进入SYN-REVD(即同步收到)的状态;
-
客户端A收到B的确认请求后,会再一次发送确认号给B,将确认号ACK置为1,确认号ack=y+1,确认报文段不携带数据的话,是不需要消耗序号的,seq=x+1。这时TCP连接已经建立,A进入ESTABLISHED(已建立连接)状态。当B收到了A的确认后,也进入ESTABLISHED状态。
TCP的四次挥手
TCP的连接释放,即TCP的四次挥手。
- 客户A向服务发送连接释放报文,将FIN位置为1,其序号seq=u+1,u表示前面已发送数据的字节值。此时A进入FIN-WAIT-1(终止等待状态1)状态,等待B的确认;
- B收到连接释放报文后即发出确认,确认号ack=u+2,seq=v,ACK=1。然后B进入CLOSE-WAIT(关闭等待)状态。此时服务B会通知高层应用进程,A到B这个方向的连接就释放掉了,这时TCP处于半关闭状态(half-close)。但是B到A方向的连接还没释放,还是可以继续发送数据的;A收到B的确认后,就进入FIN-WAIT-2(终止等待状态2)。
- 若B已经没有要向A发送的数据,应用进程会通知TCP释放连接。这是B会发出连接释放的报文,将FIN位置为1,继续发送ack=u+2的确认号。置ACK=1,序号seq=w;此后B进入LAST-BACK(最后确认)状态,等待A的确认。
- A在收到B的连接释放报文后,必须对此发出确认。将
ACK=1,ack=w+1,序号seq=u+2
的报文发送给B,B收到确认后,就进入CLOSED(关闭)状态了。然后进入TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放掉,A必须等待时间等待器设置的2MSL(RFC793 定义了MSL为2分钟,Linux设置成了30s)后,A才进入CLOSED状态。时间MSL叫做最长报文段寿命。
tcp的常见问题
1. 三次握手时,为什么需要第三次确认?
是为了防止已失效的连接请求报文段突然又传送到了服务B,从而产生了错误。这里【已失效的连接请求报文段】指的是当A发起第一个连接请求时,由于网络问题等原因没发送给B,A在等待一段时间后会重新发送报文段,这时候A就连续发送了两条请求报文段,而当连接建立后,此前因网络问题原因滞留的报文又到了服务B,服务B会认为是新的连接请求,从而再发送确认报文给A;如果不经过第三次的握手确认,那么A重传后的请求又可以建立起了连接,从而引发B资源的浪费。
2. TIME-WAIT是什么?为什么要等待2MSL?
TIME-WAIT
指的是客户端的时间等待状态;需要等待2MSL有两个原因。
一是为了保证A发送的最后一个ACK报文段能够达到B,因为这个ACK报文段可能是会丢失,因而使得处于LAST-ACK状态B收不到对已发送FIN+ACK报文段的确认;B就会超时重传这个FIN+ACK报文段,而A在等待的2MSL中就可以收到B超时重传的报文。接着A重传一次确认,重新启动2MSL计时器,最后A、B都正常进入CLOSED状态。如果A在TIME-WAIT状态不等待一段时间,而在发送完ACK报文段后立即释放连接,那么就会收不到B重传的确认报文,因而也不会再发送一次确认报文段。这样,B就无法按照正常步骤进入ClOSED状态了。
二是为了防止已失效的连接请求报文段出现在本连接中。A在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段从网络中消失。这样下一次新的连接中不会出现这种旧的连接请求报文段了。
参考文章
谢希仁 – 《计算机网络》