TCP三次握四次挥手里seq和ack号的【正确】理解
1 理论知识
先上一张图,TCP/IP详解第18章的这张图描述了一个正常的三次握手和四次挥手的状态迁移,以及seq、ack序号的变化。
基本状态看图就能了解,本文主要围绕序号的变化进行讲解。
1)seq序号
seq的初始值在不同系统实现不一样,一般为随时间增长的值。当seq超过4字节存储空间后从0开始。
在某个方向上传输N个字节的数据,序列号就+N,因此seq用于确认在某个方向上传输的字节数。
如果传输的数据字节为0,即只有首部,那序列号还增加吗?当syn 或 fin被置1,虽然报文里长度为0,计算ACK时我们可以认为其长度为1。其他情况(如只有ack)不增加。
2)ack序列号
只有ack标志置1才有效。在TCP交互的整个周期,除了syn包外其他所有包ack都被置1。
ack序列号是上个报文的序列号+已经成功收到数据长度,比如上次成功接收了seq为1,长度为1000的数据,发送的ack序号为1001,表示我seq1000以前的数据我已经成功接收了,我对序列号1001开始的数据感兴趣。
2 实例
这是一次http交互的一个例子。①~③为三次握手,④~⑥为数据传送阶段,⑦~⑨为四次挥手过程(这里实际只有三个包,因为⑧结合了fin和ack两个步骤)。
我们看第一个包的seq和ack都是0,这是wireshark为了方便阅读将序号重0开始计算(没有改变实际值),我们看到有字段里有两个带RAW的字段,它们才是序号真实的值。
注:
1)在下面的解析中,我们不会使用raw的值,但为了说明不是从0开始,我们取整数10000和20000作为两端seq的初始值。
2)192.168.31.21记为A,211.158.235.30记为B。
2.1 三次握手过程
序号变化(假设从10000和20000作为初始值):
① 首先A向B发一个SYN包,告诉B请求建立连接。seq为初始化的随机值(如何初始化和系统具体实现有关),这里假设为10000,此时ACK序号为0。
② B收到后会发一个对SYN包的确认包(SYN+ACK)回去,表示对第一个SYN包的确认,并继续握手操作。
此时B也以一个随机值来初始化seq(与A无关),这里假设为20000。B的ACK是A的seq加1,即10000+1=10001。表示你的请求我已收到,我这方的序号就从20000开始,由于你的syn包消耗了1字节,我期望下一个收到的序列号从10001开始。
③ A收到SYN+ACK 包后回一个确认包(ACK)通知B连接已建立。
它的seq是上个请求(①)的seq加1,即10000+1=10001,当然也等于上一个包(②)的ACK,ACK是B的seq加1,即20000+1=20001,用于确认收到②(syn被置位,所以也消耗了1字节)。
至此,三次握手完成,一个TCP连接建立完成。
2.2 数据传输阶段
④ A发起Get请求,由于上一个③的len=0,且没有syn或fin标志,因此没有消耗seq的值,seq不变还是10001。ACK序号确认的还是上一个收到的②(这段期间也没有收到新的包)。
⑤ B对收到的④发出回应,告诉A我已经收到。由于上一个请求②消耗了1字节,这里seq为20001。ACK为④的seq+len=10001+183=10184。
⑥ B发送200ok,由于⑤长度为0没有消耗seq,这里seq仍然为20001,ACK序号确认的还是上一个收到的④。
2.3 四次挥手
计算过程基本相同,几个注意点是:
1)A对⑥的确认合并到第一个挥手包里了。
2)⑧将FIN和ACK原本的两次挥手合并。
⑦ 确认⑥,并发出挥手。
⑧ 确认⑦,并发出挥手。⑦长度0,但是有FIN标志,消耗1字节,ACK=10184+1=10185。
⑨ 确认⑧,虽然⑧长度为0,但是有FIN标志,消耗1字节,ACK=20155+1=20156。至此挥手完成,TCP整个生命周期结束。