在TCP报头中存在32位序号(seq)和32位确认序号(ack)。
特性:
1、seq = 发送给对方数据的标识。
2、ack = 告诉对方下一次发送数据序列号 (也可以理解为已经接收到之前数据了,希望下次ack发送)
3、在三次握手的过程双方都有一个维护的序列(seq起始序列)。
4、发送、接收数据过程的序号是接着三次握手序号后面的。
5、ack = 对方发来的seq + 加上对方发来的数据长度。
seq = 对方发来的ack。
1、ack = 对方发来数据包seq + 数据长度
如果len = 0; ack = seq + 1
不为0; ack = seq + len
2、seq = 对方发来数据包的ack
下图为我抓的包 - 以此为例分析序列变化过程。
这块客户端port = 53378; 服务器port = 17777
前提: 每一次求seq序号和ack序号 需要依赖于对端发来数据包中seq、ack。 (或者是我们接收到对端发来的数据包)
seq = 对端发来数据包中ack序号
ack = 对端发来数据包中seq + len(当len为0, 默认为1)
一、三次握手:
第一次握手(客户端发送SYN数据包):seq = 0, 客户端维护seq为0。(第一次握手是没有ack序号的)
第二次握手(服务器回复SYN+ACK数据包): seq = 0; ack = 1; 服务器端维护seq为0。
seq = 0, 这是服务器端维护的seq序号, 在这块我们初始化为0。
ack = 0 + 1 = 1; 在特性中提到ack=对端发来数据包中 seq + len(如果len=0,默认为1),也就是第一次握手客户端发来的数据包。
第三次握手(客户端回复ACK数据包):seq = 1; ack = 1;
seq = 1, 对端发来数据包中ack, 也就是第二次握手服务器发送的数据包。
ack = 0 + 1, 对端发来的数据包中seq + len (如果len=0, len默认为1)。
这里我说那么多遍对端发来数据包, 也就是我们接收的数据包。 目的是为了强调这很重要, 因为我们发送数据包, 其中seq、ack序号是要参考对端发来的数据包。
重点:
1、客户端和服务器端都有一个自己维护的seq序号,并且在第一次握手、第二次握手创建。
2、一开始的维护的seq不一定为0, 这快举例设置为0。
二、数据交互过程
前提:ACK确认包不消化序号。
这是第一次客户端给服务器端发送的数据包(53378 -> 17777)。
可以看出其中的seq、ack序号是接着三次握手后的。
这块我们发送了一个len=1长度为1的数据。
这是服务器接收到客户端发来的数据包后, 返回了一个ACK确认包 + 外加一个PSH自己想要发送的数据包。 (从其中端口指向可以看出:17777 -> 53378)
记着4号数据包,也就是上一次客户端第一次发送数据包。依据上述seq、ack求法求得:
seq = 1,
ack = 1 + 1 = 2。
1、在这里我多次说对端发送的数据包, 因此这很重要, 这决定我们发送数据包seq、ack序号的取值。
2、在这里你可以看出, ACK确认包和PSH数据包seq序号、ack序号是一样的, (只不过PSH数据包多一个len,毕竟PSH是真正发送的数据包), 你会发现ACK确认包没有消耗序号。
下面序号变化只要掌握上面,相信你可以很简单的推导!
三、分析
我们可以发现抓的包没有捎带应答机制, 当对端给我们发送一个PSH数据包时, 我们会返回一个ACK确认包和一个PSH我们想要发送数据的数据包, 分了两步发送。 在网络传输中物理层有很多不可靠的因素可能会导致丢包。 多当多一个传输次数,就多一份丢包的概率。 因此tcp为了可靠传输创建了捎带应答机制, 就是ACK确认包和PSH数据包一块发送。 减少传输次数,从而减少丢包概率。
如果你抓的包时ACK确认包和PSH数据包一起传输,请注意到这点!
下面是我手绘的传输图。