TCP报文断的首部格式
TCP是面向字节流的,但是传输的数据单元是报文段。提个TCP报文段由首部和数据部构成,TCP所有的功能都体现在它首部个字段的作用。所以,先弄清TCP首部各字段的作用,才能掌握TCP的工作原理。
下面是TCP报文段首部的格式。
TCP报文段首部的前20个字节是固定的,后面有根据需要而增加的选项,所以TCP首部最小时20个字节。
首部个字段意义:
-
源端口号和目的端口号 :各占2个字节,分别写入源端口号和目的端口号。
-
序号(seq) :占4个字节,序号范围是[0, 2 32 − 1 2^{32}-1 232−1],共 2 32 2^{32} 232(4 294 967 296)个序号。当序号增加到 2 32 − 1 2^{32}-1 232−1后,下一个序号又回到0。在一个TCP传输的字节流中的每一个字节都按顺序编号。
-
确认号(ack):占4个字节,是期望收到对方下一个报文段的第一个数据字节的序号。若确认号 = N,则表示:到序号N - 1为止的所有数据都已经正确收到。
-
确认ACK:仅当ACK = 1时确认号字段才有效。当ACK = 0时确认号无效。TCP规定,在建立连接后所有传送的报文段都必须把ACK置1。
-
同步SYN:在连接时用来同步序号,SYN置为1则表示这是一个请求连接报文或确认连接报文。
-
终止FIN:用于释放连接。当FIN = 1时,表示发送方的数据传输完毕,并要求释放连接。
下面介绍的首部字段和TCP建立连接没有什么关系,可以跳过
-
数据偏移:占4位,支出TCP的数据起始处距TCP报文的起始处有多远。这个字段也表明了TCP报文首部的实际长度。这是首部有选项的存在。
-
保留:占6位,今后使用,目前应置为0
-
紧急URG:当URG = 1时,表明紧急指针有效。它告诉系统次报文中有紧急数据,需要尽快传送,而不是按原来排队的顺序传送。例如:已经发送了很长的一个程序要在远程主机上运行,但后来发现一些问题,需要取消改程序的运行。于是用户从键盘发送了中断命令(control + C)。如果不使用紧急指针的话,那么这两个字符会被放到TCP接受缓存的末尾。只有在数据处理完之后才这两个字符才被交付到应用程序,这样做浪费了很多时间。
-
推送PSH:当两个应用程序交互式的通信时,有时在一段的应用程序希望键入一个命令后就立即能收到对方的响应。这是,发送方TCP把PSH置1,并立即创建一个保温发送出去。接受方TCP收到PSH = 1的报文段,就尽快的交付到应用程序,而不再等到整个缓存填满再进行交付。
-
复位RST:当RST = 1时,表示TCP连接出现了严重的错误,必须释放连接,然后重新建立连接。
-
窗口:占2个字节,范围[0, 2 16 − 1 2^{16}-1 216−1]之间的整数。窗口值告诉对方:从报文首部中的确认号算起,接收方目前允许对方发送的数据量。窗口值作为接收方让发送方设置其发送窗口的依据。
-
校验和:占2个字节,校验和检验的范围包括首部和数据部两个部分。
-
紧急指针:占2个字节,在URG = 1时由意义。它支出本报文段中紧急数据的字节数。
-
选项:可变长度,最长可达40个字节。当没有使用“选项”选项时,TCP首部长度是20个字节。
TCP的连接建立
TCP建立连接的过程叫做握手,握手需要在客户端和服务端之间交换三个报文段。下图是建立TCP连接的过程:
- B的TCP服务进程创建传输进程控制块TCB。准备接受客户端的连接请求,服务器进出处于LISTEN状态。
- A的TCP进程也创建传输进程控制块TCB,向B发送TCP连接请求报文,这时报文首部的SYN = 1,同时选择一个序列号seq = x,SYN报文段不能携带数据,但是要消耗一个序号。此时A处于SYN-ESNT(同步已发送)状态。
- B 收到连接请求报文后,如果同意建立连接,则想A发送确认。并向A发送SYN和ACK都为1,并且确认号ack = x + 1,同时也为自己选择一个需要seq = y。这个报文段也不能携带数据,也要消耗一个序号。这是B的TCP进程处于SYN-RCVD(同步收到状态)。
- A收到B的确认后,还要想B给出确认。确认报文段的ACK置1,确认号ack = y + 1,自己的序号seq = x + 1。ACK报文段可以携带数据,但如果不携带数据则不消耗序号,在这种情况下,下一个报文段的序号仍是seq = x + 1。这时A进入ESTABLISHED(已建立连接)状态。
- B收到确认报文段后,也进入ESTABLISHED状态
为何要三次握手
为什么A要最后确认一次呢?这主要是为了防止已失效的连接突然有传到B了,产生错误。
所谓已失效的连接请求报文会如何产生呢?首先我们考虑一下正常的情况,A向B发送了一次连接请求报文,但是因报文丢失而未收到确认。于是A重新发送连接请求报文,报文成功送到并收到B的确认。于是开始数据传输,传输完毕后关闭连接。其中A发送了两次连接请求报文,一次丢失,一次成功送达,没有产生已失效的连接请求报文
但是如果A第一次发送的报文并没有丢失,只是在网络中滞留了,A再次发送连接请求报文,B收到A第二次发送的报文并确认回复,于是建立连接。但是B又收到了第一次A发送的请求报文,这本应该是已失效的连接请求报文,但B会认为A又一次发送了连接请求报文,于是B这次再回复确认A第一次发送的请求报文。这时,B认为连接建立的,并等待A传输数据,但对于A来说,这是已失效的连接请求报文,不予理睬,也不会发送数据,B却在一直等待,白白浪费资源。
TCP的连接释放
数据传输结束后,双方都可以释放连接。
- A的连接释放报文段的终止控制位FIN = 1,其序号为seq = u(u等于前面传输过的数据的最后一个字节的序号加1),这是A进入FIN-WAIT-1(终止等待1)状态。等待B确认,FIN报文段不携带数据,但要消耗一个序号。
- B收到A的连接释放报文段即发送确认,确认号ack = u + 1,自己的序号v(等于前面传输过的数据的最后一个字节的序号加1)。然后B进入CLOSE-WAIT(关闭等待)状态。
这时TCP连接处于半关闭状态,A的数据已经发送完了,但B若要发送数据,A仍要接收。
A收到来自B的确认,A进入FIN-WAIT-2(终止等待2),等待B发送释放报文段。 - 如B数据已发送完毕,则向A发送连接释放报文段,FIN = 1,B的序号w(在半关闭状态B又发送了一些数据)
。B还需重复上次已发送过的确认号ack = u + 1。B进入LAST_ACK(最后确认状态)。 - A收到B的连接释放报文后还需发送确认。确认报文段的ACK置1,确认号ack = w + 1,自己的序号seq = u+1。进入TIME-WAIT(时间等待)状态。这是TCP连接还没有释放掉,必须经过时间等待计时器设置的回见2MSL之后才会进入CLOSED状态
- B收到A的确认释放连接报文,即关闭TCP连接。
为何A在 TIME-WAIT状态要等待2MSL才关闭连接
第一,保证A的最后一次ACK报文段能顺利到达B。这个ACK报文可能丢失,使一直处于LAST_ACK状态的B收不到自己已发送的FIN+ACK报文段的确认。
B会超时重传这个FIN+ACK报文段。A在2MSL中收到这个重传的报文段,再次给出确认。如果A不等待直接关闭连接,那B可能永远收不到确认,无法按正常步骤进入CLOSED状态。
第二,防止上文提到过的已失效的连接请求报文。A发送完最后一个ACK确认后,再经过2MSL,就能使在本连中产生的报文段在网络中消失。这样可以使下一个新的连接中不会出现旧的报文段。