欢迎光临散文网 会员登陆 & 注册

什么是TCP序列号(Sequence number)?它是如何用于可靠的按序传递的?

2023-02-22 16:18 作者:杰瑞春  | 我要投稿

       这个文章专门用于回答热心好学同学的问题,我非常赞赏这样打破砂锅问到底的老铁的学习精神,@lex都原谅是屑差点问到我的知识死角,因为一般情况下我们是不会关心TCP协议的每个细节。(评论在这里)。

先把TCP段的标头(首部)格式图贴出来

TCP段header格式图

       标头中,Sequence number(Seq)有32bit,即4个字节,无论有没有携带数据负载这个都是必须传递的,TCP主要是用Seq跟踪数据段传输数据的字节范围,以确保可以检测丢失和传递数据包的顺序,如果数据丢失或者无序的到达目的地,TCP会尝试重传或者重新根据Seq恢复原始顺序。

       TCP序列号的计算方法先给个轮廓,假如传出的Seq是x,tcp数据段的长度为y,如果成功到达另一端,则下一个传出的TCP数据段的序列号就是x+y。详细例子继续往下看。

       标头中,Acknowledgment number(Ack)8个字节,它的大小表示我期望对方下次你的Seq从这个地方开始,并确认我已经接受到了之前的数据。

       标头中 Data offset 4位,最大能表示整数15,它是以32位字即 32/8=4字节为单位,表示整个header(标头/首部)的大小,也用于计算TCP数据包从那个字节位置开始属于真正的数据负载(实际携带数据)部分。

       TCP是双工操作的,意思就是Seq由客户端和服务器端建立连接的时候决定来初始序列号,我们来看下三次握手建立连接协商序列号的抓包图:

三次握手协商序列号

上交互过程:(蓝色代表跟客户的Seq有关系,红色代表跟服务器有关系)

客户端--------[SYN]  Seq=0 ---------------->服务器
客户端<------[SYN,ACK]  Seq=0 Ack=1-----服务器
客户端--------[ACK]  Seq=1  Ack=1-------->服务器

解释:
第一次握手客户端发送 seq=0,告诉服务器建立连接后“我可以从0计算序列号开始传递数据”;
第二次握手服务器回复seq=0,ack=1表示建立连接后“我也可以从0开始计算序列号传递数据,但我想让你从1开始计算序列号”。
第三次握手客户端回复seq=1,ack=1,表示我们干脆都从1开始计算吧。

       接下来我们看看请求数据的过程,这个序号是咋来的,就能搞明白TCP到底是怎么处理数据包的。先看按照http1.1协议请求一个3928字节的CSS文件看看过程(注意下图的红色框和红色字部分)。

第一个TCP请求开始

第一个请求交互过程
客户端--------[SYN]  Seq=1 Ack=1 Len=368 ---------------->服务器

解释:
        客户端发送请求要一个css文件,从协商的seq=1开始,以及确认的服务器给数据需要开始的位置Ack=1,抓包工具还给了个Len=368,代表客户端请求的TCP数据包大小是368字节。

        我去,这个Len(TCP Segment Len)的大小哪来的,TCP Header里没定义这个帧头字段啊?!这就是我为啥上图中点开了网络层IP header部分的原因(注意红框),IP协议里有IP协议包大小的字段Total Length(这里是408字节),IP的header(首部)大小说20字节,TCP的头部大小这次请求中也是20字节,那么 408-20-20=368字节,那么到了目的地,携带的数据大小不就有了嘛!

接下来按照开始说的Seq的计算方式,我们根据图看看剩下的交互部分:

TCP请求数据过程

按上图中手写的数字顺序,是整个css文件完成传输的过程。css文件交互完成总共六步:

交互过程:
客户端--------[PSH ACK]  Seq=1 Ack=1 Len=368-------->服务器
客户端<------[ACK]  Seq=1 Ack=369 Len=1412------------服务器
客户端--------[ACK]  Seq=369  Ack=1413 Len=0 --------->服务器
客户端<------[ACK]  Seq=1413 Ack=369 Len=1412--------服务器
客户端<------[PSH ACK]  Seq=2825 Ack=369 Len=1104---服务器
客户端--------[ACK]  Seq=369  Ack=3929 Len=0 ---------->服务器

解释:

  1. 客户端发送请求,带上了标职位PSH,它代表直接将缓冲区的数据推送给接收方,一般没有特别意义,所以目前请求和响应最后一次的数据会加入PSH标志位;以及带上了数据负载大小为368字节。

  2. 服务器则直接Ack=368+1=369代表数据接受成功,并带上分割数据包第一部分1412自己发送给客户端。

  3. 客户端收到后,直接响应服务器确认的字节为它的Seq=369,因为它确认没携带多余的数据,并确认我收到了数据,给服务器说你可以从1413继续回复给我。

  4. 服务器继续携带第二部分的TCP数据包,携带1412自己的数据,并将Seq=1413,告诉客户的,我按你期望的顺序,从1413字节开始再发给你1412字节的数据。

  5. 服务器发现剩下最后一部分1104字节的数据了,不等客户端确认,直接推送给客户的,并设置Seq=2825.

  6. 客户端回复Ack=3929(第四步seq1413+第四步Len1412+第五步Len1104),告诉服务器第四第五步给我的数据都收到 。

最后总结看下图:表示这次请求分成了个TCP数据包,每个包的数据负载从x字节到x自己的数据范围,重新组装的数据大小是3928字节。省下就交给应用程序层处理了!

TCP总结图

        最后再补充点TCP数据包不可能大于1460字节的知识,因为数据到达下一层网络层,数据超过1500字节会被网络层再次分包,一般TCP数据包的分割最大大小(MSS-maximun segment size)可能就是1500字节-IP头20字节-TCP头最少20字节=1460字节,就能保证TCP的数据包不需要下一层网络层做分包的工作了,避免分包错误丢弃数据的风险。其实在建立连接三次握手的时候,双方已经协商了MSS的值了,就藏在标头option字段里,见下图。

协商TCP数据包段最大大小


   讲完了,不明白的欢迎留言评论😂

什么是TCP序列号(Sequence number)?它是如何用于可靠的按序传递的?的评论 (共 条)

分享到微博请遵守国家法律