我们知道,TCP协议是通过ACK来确保报文有序的。
序列号(Sequence Number)字段标识了TCP发送端到TCP接收端的数据流的一个字节,该字节代表着包含该序列号的报文段的数据中的第一个字节。
ack的作用
确认号
字段(也简称ACK号或ACK字段)包含的值是该确认号的发送方期待接收的下一个序列号。即最后被成功接收的数据字节的序列号加1。
ack是累计的
ACK是累计的,表示的是已收到的有序字节流的最后一个字节+1
。
累加的字节流数据,包含哪些数据?
我们还是通过实例来说明,看下面一个示例,一个普通的http请求,先看一下Client发送个Server的请求报文:
如上图所示:
它的Sequence Number为926731958,http层报文的长度为931。
需要注意的是,这里的Length
指的是tcp上层的数据长度,本示例中是http层的数据,不包含tcp header、ip header 和 数据链路层的header。
如果这个报文发送成功,下次接收到的ACK应该是926732889(926731958 + 931)。
我们看下对应的响应报文,验证下这个逻辑:
如上图所示,Server端发送给Client端的ACK是926732889(926731958 + 931),等于Server端上一个接收到的报文的起始序列号(926731958)+ 报文长度(931)。
同理,当前Server端发送给Client端的报文的序列号和长度分别是796375356和195,所以下一次Client发送的ACK中应该是796375356 + 195 = 796375551。
我们看下下一个报文:
bigo,完全符合预期。
我们用图例来表示一下:
总结一下,
当前报文对应的ACK = 当前报文的第一个字节的序列号 + 报文长度(http层)
。
当然了,这里的示例是最简单的场景。因为我们的ACK的发送时间,不是收到报文后立即发送的。
当TCP接收到连接的另一端的数据时,它会发送一个ACK。这个确认可能不会立即发送
,而一般会延迟
片刻。TCP使用的ACK是累积
的,从某种意义来讲,一个指示字节号IV的ACK暗示着所有直到IV的字节(但不包含IV)已经成功被接收了
。
实际的场景中,可能返回的ACK大于我们这里计算的数字,也是合理的,因为ACK表示的是之前的数据都是正确、有序收到了。
三次握手、四次挥手中的ACK变化
发送在本次连接的这个方向上的数据的第一个字节的序列号是ISN加1,因为SYN位字段会消耗一个序列号。消耗一个序列号也意味着使用重传进行可靠传输。因此,SYN和应用程序字节(还有FIN)是被可靠传输的。不消耗序列号的ACK则不是。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CSMQzP9y-1646284881642)(evernotecid://5E413247-DCB6-499B-8453-F2A2C2DF33A4/appyinxiangcom/13182898/ENResource/p18380)]
上图是经典的三次握手、四次挥手的示意图,可以看出,三次握手中,第一阶段的SYN确实是消耗掉了一个序列号,因为Client在收到Server的ACK之后,第三次握手的报文中,序列号Seq变为了ISN(c)+1。
在关闭连接过程中,第二次握手和第三次握手的ACK都是L+1,因为这期间没有收到新的报文;再看第四次握手,Client即使已经收到了ACK为L+1,但是它发送出去的Seq依然是K,跟第一次握手一样的序列号,表明ACK不消耗序列号。
参考
TCP/IP详解 卷1:协议 原书第二版
确认消息
Acknowledgement (data networks)