TCP滑动窗口

概要

1.jpg TCP的Window表头用来指定滑动窗口的大小,单位是字节。因为Window占16位,所以TCP的标准滑动窗口最大为2^16-1=65535个字节。另外,Options表头包含了滑动窗口的扩大因子。
滑动窗口的主要作用有:

  • 流量控制:
    发送方不能以超过接收方所能接受的速率发送分段,主要的方式是:接收方在返回ACK的时候,会包含自己的接收窗口的大小,进而控制发送方的发送窗口的大小
  • 保证TCP的可靠性:
    可靠性是建立在“确认重传”的基础上的

发送窗口

在任何时刻,TCP会话的发送方的缓冲区中的数据都可以分为四类:

  • 已发送 并且 得到对端ACK的
  • 已发送 但是 未收到对端ACK的
  • 未发送 但是 对端允许发送的
  • 未发送 并且 对端不允许发送的

已发送 但是 未收到对端ACK的未发送 但是 对端允许发送的,这两部分数据称之为发送窗口

1.jpg 上图中的 提议窗口 就是接收方ACK的时候,通报的窗口。
只有在收到对端的ACK的时候,发送窗口的左边缘才会移动。并且当收到的ACK要求左边缘左移的时候,会被丢弃,因为它是一个重复的确认。
窗口两端的运动会增加或减少窗口的大小。我们使用三个术语描述窗口边缘的左右运动:

  • 窗口闭合(window closes):窗口的左边缘向右移动。窗口闭合发生在 已发送分段 并 收到对端的ACK的情况下
  • 窗口打开(window opens):窗口的右边缘向右移动。当接收端的应用进程读取已确认的数据的时候,接收端的接收窗口的右边缘就会向右扩张,实际上接收窗口是一个环行的缓冲区,接收窗口的右边缘扩张会使用原来被应用进程取走内容的缓冲区。接收窗口扩张后会使用ACK通知发送方,进而导致发送方的发送窗口打开;因为ACK的序号仍然是上次的,所以发送窗口不会闭合
  • 窗口收缩(window shrinks):窗口的右边缘向左移动。RFC不鼓励这种做法,但是TCP必须能够处理这种情况
    1.jpg
    如果窗口的左边缘和右边缘重合,就称它为“零窗口”。它将停止发送者传输任何数据。

接收窗口

在某一时刻,TCP会话的接收方的缓冲区,可以分为三部分:

  • 保存已接收的数据的部分(因为ACK是由TCP协议栈直接回复的,无应用延迟,所以不存在已接收,但未ACK的情况)
  • 允许接收数据的部分
  • 不允许接收数据的部分

其中,允许接收数据的部分就是接收窗口。
1.jpg
接收窗口的移动:

  • 窗口闭合:
    接收窗口的左边缘向右移动。在收到发送方的数据之后,接收方会确认数据的准确性,然后把数据存储到缓冲区中。需要注意的是:接收窗口只会确认连续的分组,对于乱序的分组,则是先接收下来,避免重复传送。 在确认连续的分组之后,因为数据还没有被应用进程取走,此时就需要进行窗口的闭合
  • 窗口打开:
    参考发送窗口的窗口打开
  • 窗口收缩:
    接收窗口的右边缘向左移动。RFC不鼓励这种做法,但是TCP必须能够处理这种情况

发送窗口 和 接收窗口的关系

TCP是全双工的,可以同时传送和接收数据,因为TCP会话的两端各自维护一个接收窗口 和 发送窗口。其中,接收窗口的大小受限于应用程序、硬件、系统等;而发送窗口的大小则取决于对端通告的接收窗口。


滑动窗口和socket缓冲区的关系

  • TCP的滑动窗口的大小实际上就是socket的接收缓冲区的大小
  • 对于server端的socket一定要在listen之前设置缓冲区大小,因为,accept时新产生的socket会继承监听socket的缓冲区大小。对于client端的socket一定要在connet之前设置缓冲区大小,因为connet时需要进行三次握手过程,会通知对方自己的窗口大小。在connet之后再设置缓冲区,已经没有什么意义

参考文档

感谢浏览tim chow的作品!

如果您喜欢,可以分享到: 更多

如果您有任何疑问或想要与tim chow进行交流

可点此给tim chow发信

如有问题,也可在下面留言: