一、关于滑动窗口协议
在 TCP 协议中,所有的 SEQ 包发送出去都必须要受到对方的 ACK 才认为是发送成功,如果长时间没有收到 ACK 回复确认,发送方需要重新发送该包。而如果发送方每次都是发送一个包,然后等到接收方回复 ACK 了再发送下一个包,那么数据包的传输效率就相当低了。滑动窗口协议的作用就是为了解决这个问题。
在滑动窗口协议中,发送方可以同时发送多个数据包并可以不用等待接收方确认,这样就大大增加了网络的推图两。唯一的限制是:接收方未确认的数据包不得超过双方约定的窗口个数。 TCP 包头中有一个两字节的字段表示滑动窗口的大小,因此最大的滑动窗口大小为 65535,这个大小是由接收方提出的,以接收方的大小为准。
最大窗口 65536 并不是绝对的,TCP 选项中有提供一个窗口放大的选项可以对这个数值缩放。
例如以下数据,将发送的字节从 1 至 11 进行标号,接收方提出的窗口大小为 6,此时已发送的 1/2/3 字节数据被接收方确认,当前窗口覆盖了从第 4 字节到第 9 字节的区域。 4/5/6 字节是已经发送但是还未收到确认,此时发送方还可以发送的数据域只剩下三个,即 7/8/9 三个数据。当 7/8/9 发送完成后,发送方无法再继续发送数据。只有接收方再确认序号 4 的数据后,发送方才能继续发送数据 10 。此时可用的窗口将会向右延伸到 10,同时左边已发送并确认的数据也会向右延伸到 4 。
这个过程看起来就像是一个窗口不停向右滑动,因此该协议被称为滑动窗口协议。
滑动窗口涉及到以下几个概念:
- 窗口合拢:当窗口左边界向右靠近时,这种现象发生在数据被发送方确认时。
- 窗口张开:窗口的右边界向右移动的时候,这种现象发生在接收端处理的数据的时候。
- 窗口收缩:窗口右边界向左移动时,这种现象不常发生。
注意:窗口大小是由接收方通告的,通过采取慢启动和拥塞避免算法等机制来使带宽和性能取得最佳。
二、坚持定时器
2.1 坚持定时器的引入
思考以下场景:接收方的接收缓冲区已满,因此接收方发送了一个窗口大小为 0 的数据到发送方,此时发送方停止发送数据。经过一段时间后,接收方有空闲的缓冲区可以接收数据,此时给发送方发了一个窗口大小不为 0 的包,但是恰巧这个包在路上出现了意外没有到达发送方。此时发送方还在一直等待接收方发送不为 0 的窗口过来,但是接收方又在等待发送方继续发送数据。两者相当于发生了死锁。
2.2 工作原理
为了解决上面这个问题,TCP 引入了坚持定时器,该定时器功能为:TCP 连接的一方收到对方窗口为 0 的通知时,启动该定时器,若定时器持续时间到达后还没有收到对方窗口大小不为 0 的通知,主动发送一个零窗口探测包 (仅携带 1 字节数据),对方则需要在这个包中回复当前窗口的大小。
三、关于 window scale 选项
window scale
选项是提供给滑动窗口协议用于对窗口大小放大使用,在 SYN
包或者 SYN,ACK
包中作为选项字段,表示滑动窗口的实际大小要根据这个值来进行缩放 (SYN 包本身的窗口不会缩放) 。
发送方和接收方的滑动窗口缩放因子大小可以不一样,两者互不干扰,但实际上窗口大小还是以接收方为准。使用 wireshark 抓包很容易就能看到这个缩放选项,以下是一个数据包示例。
这个包是客户端发起的三次握手数据包:
客户端设置了窗口缩放大小为 256
,因此后面实际计算的时候窗口大小要乘以这个缩放大小 1025
(使用 wireshark 很容日就能看到),乘以 256 后得到实际的窗口大小为 262400
:
Calculated window size: wireshark 根据缩放因子计算出来的实际窗口大小。
Window size scaling factor: 窗口缩放因子。
这两个参数都是 wireshark 自动生成的,并不是携带在 TCP 包中的字段。
评论