TCP 中的Sequence Number

在这里插入图片描述
我们关注的就是序号和确认号

这二者也是 TCP 实现可靠传输的方式。下图是一次随便抓包的截图(相对序列号)

意义

在这里插入图片描述
在TCP传输中,每一个字节都是有序号的,从0开始。通过序号的方式保存数据的顺序,接收端接受到之后进行重新排列成为需要的数据。

因此,我对于SEQ和ACK的了解就是:

SEQ 代表:发送的这个包中第一个字节(如果有payload的话)的序号

ACK 代表:已成功接受序列号到 ack-1 的数据,期望接收的下一个字节的序号为 ack

举例说明:

我已经发送了前100字节的数据,那么我下一个发送的包(如果发送窗口还有空间)的SEQ就是101,比如要发送10字节的数据,那么下一个包中的数据的字节编号就是 101 - 110. 之后如果继续发送的话,序号就是从111开始。

如果接收端接到了这个10字节的包的话,便会返回一个 ACK 为 111 的包,表示前面110个字节已经成功接收。

为什么SYN和FIN会消耗一个序列号
细心的同学可能会发现,为什么在建立连接的时候,发送的 SYN 包大小(payload)明明是0字节,但是接收端却返回 ACK = 1 ,还有断开连接的时候 FIN 包也被视为含有1字节的数据。

原因是 SYN 和 FIN 信号都是需要 acknowledgement 的,也就是你必须回复这个信号,如果它不占有一个字节的话,要如何判断你是回复这个信号还是回复这个信号之前的包呢?

例如:如果 FIN 信号不占用一个字节,回复 FIN 的 ack 包就可能被误认为是回复之前的数据包被重新发送了一次,第二次挥手无法完成,连接也就无法正常关闭了。

为什么SYN和ACK的初始值(ISN initialization sequence number)是一个随机值
参考TCP 的那些事儿(上)

ISN是不能hard code的,不然会出问题的——比如:如果连接建好后始终用1来做ISN,如果client发了30个segment过去,但是网络断了,于是 client重连,又用了1做ISN,但是之前连接的那些包到了,于是就被当成了新连接的包,此时,client的Sequence Number 可能是3,而Server端认为client端的这个号是30了。全乱了。RFC793中说,ISN会和一个假的时钟绑在一起,这个时钟会在每4微秒对ISN做加一操作,直到超过2^32,又从0开始。这样,一个ISN的周期大约是4.55个小时。因为,我们假设我们的TCP Segment在网络上的存活时间不会超过Maximum Segment Lifetime(缩写为MSL – Wikipedia语条),所以,只要MSL的值小于4.55小时,那么,我们就不会重用到ISN。

什么是TCP segment of a reassembled PDU

在这里插入图片描述
如图所示,在抓包的时候,经常会看到[TCP segment of a reassembled PDU ] 字样的包,这个代表数据在传输层被分包了。也就是代表包大小大于MTU,此处放一下MTU与MSS区别:

MTU(Maximum Transmission Unit)最大传输单元,在TCP/IP协议族中,指的是IP数据报能经过一个物理网络的最大报文长度,其中包括了IP首部(从20个字节到60个字节不等),一般以太网的MTU设为1500字节,加上以太帧首部的长度14字节,也就是一个以太帧不会超过1500+14 = 1514字节。

MSS(Maximum Segment Size,最大报文段大小,指的是TCP报文(一种IP协议的上层协议)的最大数据报长度,其中不包括TCP首部长度。MSS由TCP链接的过程中由双方协商得出,其中SYN字段中的选项部分包括了这个信息。如果MSS+TCP首部+IP首部大于MTU,那么IP报文就会存在分片,如果小于,那么就可以不需要分片正常发送。

因此,出现这种现象的原因就是你调用一次send的时候,send的数据比 MSS 还要打,因此就被协议栈进行了分包。

顺便说一下,IP数据包的分片是通过flag字段和offset字段共同完成的。

从图中可以看到,第6个和第5个包是同一个TCP报文被分成了两个包。如果我们点开看的话,可以看到两个报文的ACK序号都一样,并且这些报文的Sequence Number都不一样,并且后一个Sequence Number为前一个Sequence Number加上前一个报文大小再加上1 。这也是判断reassembled 的方式。

点开第6个包,可以看到它将5和6的数据整合起来了。
本文为转载文章

ISN

三次握手的一个重要功能是客户端和服务端交换ISN(Initial Sequence Number), 以便让对方知道接下来接收数据的时候如何按序列号组装数据。

如果ISN是固定的,攻击者很容易猜出后续的确认号。

ISN = M + F(localhost, localport, remotehost, remoteport)
M是一个计时器,每隔4微秒加1。 F是一个Hash算法,根据源IP、目的IP、源端口、目的端口生成一个随机数值。要保证hash算法不能被外部轻易推算得出。

序列号回绕

因为ISN是随机的,所以序列号容易就会超过2^31-1. 而tcp对于丢包和乱序等问题的判断都是依赖于序列号大小比较的。此时就出现了所谓的tcp序列号回绕(sequence wraparound)问题。怎么解决?

/** The next routines deal with comparing 32 bit unsigned ints 
 * and worry about wraparound (automatic with unsigned arithmetic).*/ 
 
static inline int before(__u32 seq1, __u32 seq2){    
     return (__s32)(seq1-seq2) < 0; 
} 
#define after(seq2, seq1) before(seq1, seq2) 

上述代码是内核中的解决回绕问题代码。s32是有符号整型的意思,而u32则是无符号整型。序列号发生回绕后,序列号变小,相减之后,把结果变成有符号数了,因此结果成了负数。

为了方便说明,我们以unsigned char和char为例来说明:
假设,seq1=255, seq2=1(发生了回绕)。 
seq1 = 1111 1111 seq2 = 0000 0001 
我们希望比较结果是 
 seq1 - seq2=  
 1111 1111 
 -0000 0001 
 -----------  
 1111 1110 
 由于我们将结果转化成了有符号数,由于最高位是1,因此结果是一个负数, 
 负数的绝对值为 0000 0001 + 1 = 0000 0010 = 2因此seq1 - seq2 < 0 

注意:

如果seq2=128的话,我们会发现:

 seq1 - seq2=

 1111 1111

-1000 0000

-----------

 0111 1111

此时结果尤为正了,判断的结果是seq1>seq2。因此,上述算法正确的前提是,回绕后的增量小于2^(n-1)-1。

由于tcp序列号用的32位无符号数,因此可以支持的回绕幅度是2^31-1,满足要求了。

这段ISN解决回绕的问题没看懂,以后再研究。

原文链接
https://www.cnblogs.com/JenningsMao/p/9487252.html

参考文档
TCP头时间戳选项与回绕序列号

http://network.51cto.com/art/201901/591114.htm

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值