TCP连接建立与释放

TCP是一个面向连接的传输层协议,所以无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条传输连接。

一、TCP首部

TCP数据在IP数据报中的位置


TCP数据结构

TCP报文段首部的前20个字节是固定的,后面有4N字节是根据需要而增加的选项。因此TCP报文段的最小长度为20个字节。

首部固定部分的各字段的意义如下:

1、源端口和目的端口:加上IP首部的源IP地址和目的IP地址,确定唯一的一个TCP连接。另外通过目的端口来决定TCP将数据报交付于那个应用程序,从而实现TCP的分用功能。

2、序号:占4个字节,序号的范围为[0,4284967296]。由于TCP是面向字节流的,在一个TCP连接中传送的字节流中的每一个字节都按顺序编号,首部中的序号字段则是指本报文段所发送的数据的第一个字节的序号。另外,序号是循环使用的,当序号增加到最大值时,下一个序号就又回到了0。

3、确认号:当ACK标志位为1时有效,表示期望收到的下一个报文段的第一个数据字节的序号。确认号为N,则表明到序号N-1为止的所有数据字节都已经被正确地接收到了。

4、头部长度:TCP报文段的头部长度,它指出TCP报文段的数据部分的起始位置与TCP报文段的起始位置的距离。头部长度占4个字节,但它的单位是32位字,即以4字节为计算单位,因此头部长度的最大值为15*4=60个字节,这就意味着选项的长度不超过40个字节。

5、保留位:必须为0.

6、下面的六个控制位说明报文段的性质:

1)URG:与首部中的紧急指针字段配合使用。URG为1时,表明紧急指针字段有效,发送应用进程告诉发送方的TCP有紧急数据要传送,于是发送方TCP就把紧急数据插入到本报文段数据的最前面,而其后面仍是普通数据。

2)ACK:仅当ACK=1时确认号字段才有效,当ACK=0时,确认号无效。TCP规定,在连接建立后所有的传送报文段都必须把ACK置1。

3)PSH:如果发送的报文段中PSH为1,则接收方接受到该报文段后,直接将其交付给应用进程,而不再等待整个缓存都填满后再向上交付。

4)RST:复位标志,RST=1时,表明TCP连接中出现严重差错,必须释放连接,然后重新建立运输连接。

5)SYN:同步序号,用来发起一个连接。当SYN=1而ACK=0时,表明这是一个连接请求报文段,若对方同意建立连接,则应在响应的报文段中使SYN=1和ACK=1。

6)FIN:用来释放一个连接。当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放连接。

7、窗口:接收方让发送方下次发送报文段时设置的发送窗口的大小。

8、校验和:校验的字段范围包括首部和数据这两部分。

9、紧急指针:紧急指针当URG=1时才有效,它指出本报文段中的紧急数据的字节数。值得注意的是,即使窗口为0时,也可发送紧急数据。

10、选项与填充:选项应该为4字节的整数倍,否则用0填充。最常见的可选字段是最长报文大小MSS(Maximum Segment Size),每个连接方通常都在通信的第一个报文段中指明这个选项。它指明本端所能接收的最大长度的报文段。该选项如果不设置,默认为536(20+20+536=576字节的IP数据报),其中ip首部和tcp首部各20个字节,而internet 上标准的MTU (最小)为576B。

 

二、TCP传输连接建立

单方主动连接的TCP连接建立过程

在TCP/IP协议体系结构中的TCP协议也是使用三次握手(three-way handshake)机制来建立传输连接的。具体流程如下图所示,具体步骤如下:

(1)首先是服务器初始化的过程,从CLOSED(关闭)状态开始通过顺序调用SOCKET、BIND、LISTEN和ACCEPT原语创建Socket套接字,进入LISTEN(监听)状态,等待客户端的TCP传输连接请求。

(2)客户端最开始也是从CLOSED状态开始调用SOCKET原语创建新的Socket套接字,然后在需要再调用CONNECT原语,向服务器发送一个将SYN字段置1(表示此为同步数据段)的数据段(假设初始序号为i),主动打开端口,进入到SYN SENT(已发送连接请求,等待对方确认)状态。

 TCP传输连接建立的三次握手过程

   (3)服务器在收到来自客户端的SYN数据段后,发回一个SYN字段置1(表示此为同步数据段),ACK字段置1(表示此为确认数据段),ack(确认号)=i+1的应答数据段(假设初始序号为j),被动打开端口,进入到SYN RCVD(已收到一个连接请求,但未进行确认)状态。这里要注意的是确认号是i+1,而不是i,表示服务器希望接收的下一下数据段序号为i+1。

(4)客户端在收到来自服务器的SYN+ACK数据段后,向服务器发送一个ACK=1(表示此为确认数据段),序号为i+1,ack=j+1的确认数据段,同时进入ESTABLISHED(连接建立)状态,建立单向连接。要注意的是,此时序号为i+1,确认号为j+1,表示客户端希望收到服务器的下一个数据段的序号j+1。

(5)服务器在收到客户端的ACK数据段后,进入ESTABLISHED状态,完成双向连接的建立。

连接可以由任一方或双方发起,一旦连接建立,数据就可以双向对等地流动,而没有所谓的主从关系。三次握手是连接两端正确同步的充要条件,因为TCP建立在不可靠的分组传输服务之上,报文可能丢失、延迟、重复和乱序,因此协议必须使用超时和重传机制。如果重传的连接请求和原先的连接请求在连接正在建立时到达,或者当一个连接已经建立、使用和结束之后,某个延迟的连接请求才到达,就会出现问题。采用三次握手协议就可以解决这些问题。如客户端发送的ACK数据段就是为了避免因网络延迟而导致的重复连接,因为这时客户端就可通过检查ACK数据段中的确认号就可得知该连接请求是否已失效。

问题:为什么一定要进行三次握手呢?

前两次的握手很显然是必须的,主要是最后一次,即客户端收到服务端发来的确认后为什么还要想服务端再发送一次确认呢?这主要是为了防止已失效的请求报文段突然又传送到了服务端而产生连接的误判。

考虑如下的情况:客户端发送了一个连接请求报文段到服务端,但是在某些网络节点上长时间滞留了,而后客户端又超时重发了一个连接请求报文段该服务端,而后正常建立连接,数据传输完毕,并释放了连接。如果这时候第一次发送的请求报文段延迟了一段时间后,又到了服务端,很显然,这本是一个早已失效的报文段,但是服务端收到后会误以为客户端又发出了一次连接请求,于是向客户端发出确认报文段,并同意建立连接。假设不采用三次握手,这时服务端只要发送了确认,新的连接就建立了,但由于客户端比你更没有发出建立连接的请求,因此不会理会服务端的确认,也不会向服务端发送数据,而服务端却认为新的连接已经建立了,并在一直等待客户端发送数据,这样服务端就会一直等待下去,直到超出保活计数器的设定值,而将客户端判定为出了问题,才会关闭这个连接。这样就浪费了很多服务器的资源。而如果采用三次握手,客户端就不会向服务端发出确认,服务端由于收不到确认,就知道客户端没有要求建立连接,从而不建立该连接。

三、TCP传输连接的释放

TCP 连接建立起来后,就可以在两个方向传送数据流。当 TCP的网络应用进程再没有数据需要发送时,就可以发出关闭连接命令,释放连接。TCP协议是通过发送FIN字段置1的数据段来作为关闭传输连接的命令,关闭本端数据流的,但是本端仍然还可以继续接收来自对端的数据,直到对端也使用了同样的方法关闭那个方向的数据流,这时整个双方传输连接就彻底关闭了。

单方主动关闭的TCP连接释放过程

相对TCP传输连接建立的三次握手过程来说,TCP传输连接的释放过程要稍微复杂一些,需要经过四次握手过程。这是因为TCP的半关闭(half-close)特性造成的,即因这一个TCP连接是全双工(即数据在两个方向上能同时传递),每个方向必须单独地进行关闭。TCP传输连接关闭的原则是:当一方完成它的数据发送任务后就可以发送一个FIN字段置1的数据段来终止这个方向的数据发送;当另一端收到这个FIN数据段后,必须通知它的应用层“对端已经终止了那个方向的数据传送”。而FIN数据段的发送是由应用层调用CLOSE服务原语的结果。TCP连接释放的四次握手过程如下图所示,具体描述如下:

(1)一开始,通信双方都处于ESTABLISHED(连接建立)状态。如果客户端认为数据全部发送完了,想结束本次传输连接,则由应用层的对应应用进程调用CLOSE服务原语,然后向服务器发出一个FIN字段置1的数据段(假设此数据段的序号为m),客户端进入FIN WAIT 1状态,等待服务器的确认。

(2)服务器在收到客户端发来的FIN数据段后,确认客户端没有新的数据要发送了,向客户端发送一个ACK字段置1,确认号为m+1(假设此数据段序号为w,服务器与客户端的数据段序号可以不一样),表示前面的数据已全部收到了,然后进入到CLOSE WAIT(关闭等待)状态。与此同时服务器的TCP实体通知对应的应用层进程,释放从客户机到服务器方向的传输连接,进入半关闭状态。但此时服务器仍可以向客户端发送数据段;客户端也可接收来自服务器的数据。而且这可能要持续一段时间,直到服务器的数据也全部发送完。

 TCP传输连接释放的四次握手过程

    (3)当客户端收到服务器的ACK数据段后便进入到了FIN WAIT 2状态,进一步等待服务器发出连接释放的数据段。

(4)当服务器发送完全部的数据后,其对应的应用进程也会通知TCP实体释放此方向的TCP传输连接,向客户机发送FIN字段置1,ACK字段置1,ack=m+1(假设此时的数据段序号已变为w)的确认数据段。这时服务器进入LAST ACK(最后确认)状态,等待客户端的确认。

(5)客户端在收到服务器的FIN+ACK数据段后,向服务器发送一个ACK字段置1,ack=w+1,序列号为m+1的数据段,进入到TIME WAIT状态。但此时TCP连接还没有释放,必须等待2MSL时间(RFC 793建议设MSL为2分钟)后,客户端才进入到CLOSED状态,彻底释放了TCP连接。

(6)服务器在收到客户端发来的ACK数据段后,也进入CLOSED状态,彻底释放连接。完成整个TCP传输连接释放过程。

问题:为什么A在TIME—WAIT状态必须等待2MSL时间呢?

1、为了保证A发送的最后一个ACK报文段能够到达B。该ACK报文段很有可能丢失,因而使处于在LIST—ACK状态的B收不到对已发送的FIN+ACK报文段的确认,B可能会重传这个FIN+ACK报文段,而A就在这2MSL时间内收到这个重传的FIN+ACK报文段,接着A重传一次确认,重新启动2MSL计时器,最后A和B都进入CLOSED状态。如果A在TIME—WAIT状态不等待一段时间就直接释放连接,到CLOSED状态,那么久无法收到B重传的FIN+ACK报文段,也就不会再发送一次确认ACK报文段,B就无法正常进入CLOSED状态。

2、防止已失效的请求连接出现在本连接中。在连接处于2MSL等待时,任何迟到的报文段将被丢弃,因为处于2MSL等待的、由该插口(插口是IP和端口对的意思,socket)定义的连接在这段时间内将不能被再用,这样就可以使下一个新的连接中不会出现这种旧的连接之前延迟的报文段。

补充:

当客户端执行主动关闭并进入TIME—WAIT是正常的,服务端执行被动关闭,不会进入TIME—WAIT状态,这说明,如果终止了一个客户程序,并立即重启该客户程序,则新的客户程序将不再重用相同的本地端口,而是使用新的端口,这不会带来什么问题,因为客户端使用本地端口,而并不关心这个端口是多少。但对于服务器来说,情况就不同了,服务器总是用我们熟知的端口,那么在2MSL时间内,重启服务器就会出错,为了避免这个错误,服务器给出了一个平静时间的概念,这是说在2MSL时间内,虽然可以重新启动服务器,但是这个服务器还是要平静的等待2MSL时间的过去才能进行下一次连接

参考:
基础教程:TCP连接的建立和释放
TCP传输连接建立与释放详解
TCP-IP详解(卷一、二、三)