窗口滑动/拥塞控制/流量控制

滑动窗口

对于确认应答策略,每一个发送的数据段,都要给一个ACK确认应答,收到ACK后再发送下一个数据段。缺点是性能较差,尤其是数据往返时间较长的时候

窗口滑动/拥塞控制/流量控制

既然一发一收的方式性能较低,那么一次性发送多条数据,就可以大大提高性能(其实就是将多个段的时间重叠在一起)

窗口滑动/拥塞控制/流量控制

  • 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值,上图的窗口大小就是4000个字节(四个段)
  • 发送前四个段,不需要等到任何确ACK,直接发送
  • 收到第一个ACK之后,窗口向后移动,继续发送第五个段的数据,以此类推
  • 操作系统内核为了维护这个窗口,需要开辟发送缓冲区,记录当前还有哪些数据没有应答;只有确认过应答的数据,再能从缓冲区中移除
  • 窗口越大网络的吞吐率越高

窗口滑动/拥塞控制/流量控制

那么如果出现了丢包,如何进行重传

情况一:数据包已经抵达ACK丢了

窗口滑动/拥塞控制/流量控制

这种情况下ACK丢了也不要紧,因为可以通过后续ACK进行确认

情况二:数据包直接丢了

窗口滑动/拥塞控制/流量控制

  • 当某一段报文段丢失之后,发送端会一直就收到1001这样的ACK,就像是提醒发送端“我想要的是1001”一样
  • 如果发送端主机连续三次收到同样一个“1001”这样的应答,就会将对应的数据1001-2000重新发送
  • 这个时候接收端接收到1001之后,再次返回的ACK就是7001(因为2001-7000)接收端其实之前已经收到了,被放到接收端的操作系统内核的接受缓冲区当中
  • 这种机制也被叫做快速重传机制

 拥塞控制

  • 虽然TCP有了滑动窗口机制,能够高效可靠的发送大量数据,但是如果在刚开始的时候就发送大量的数据,仍然可能引发问题
  • 因为网络上有很多计算机,可能由于当前网络状态比较拥堵,在不清楚当前网络的状态下,贸然发送大量数据,是可能会导致雪上加霜
  • TCP引入了慢启动机制,先发送少量的数据,探探路,摸清当前网络状况,再决定按照多大速度传输

窗口滑动/拥塞控制/流量控制

  • 此处引入一个概念程为拥塞窗口
  • 发送开始的时候, 定义拥塞窗口大小为1
  • 每次收到一个ACK应答, 拥塞窗口加1
  • 每次发送数据包的时候, 将拥塞窗口和接收端主机反馈的窗口大小做比较, 取较小的值作为实际发送的窗口;
  • 像上面这样的拥塞窗口增长速度, 是指数级别的. "慢启动" 只是指初使时慢, 但是增长速度非常快
  • 为了不增长的那么快, 因此不能使拥塞窗口单纯的加倍
  • 此处引入一个叫做慢启动的阈值
  • 当拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长

窗口滑动/拥塞控制/流量控制

  • 当TCP开始启动的时候,慢启动的阈值等于窗口的最大值
  • 在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回1;
  • 少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞
  • 当TCP通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降
  • 拥塞控制, 归根结底是TCP协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案

流量控制

接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这个时候如果发送端继续发送,
就会造成丢包, 继而引起丢包重传等等一系列连锁反应

因此TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制(Flow Control);

  • 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 "窗口大小" 字段, 通过ACK端通知发送端
  • 窗口大小字段越大, 说明网络的吞吐量越高
  • 接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端
  • 发送端接受到这个窗口之后, 就会减慢自己的发送速度
  • 如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数
    据段, 使接收端把窗口大小告诉发送端

窗口滑动/拥塞控制/流量控制

TCP首部中, 有一个16位窗口字段, 就是存放了窗口大小信息;那么问题来了, 16位数字最大表示65535, 那么TCP窗口最大就是65535字么?
实际上, TCP首部40字节选项中还包含了一个窗口扩大因子M, 实际窗口大小是 窗口字段的值左移 M 位;

延迟应答

  • 如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小.
  • 假设接收端缓冲区为1M. 一次收到了500K的数据; 如果立刻应答, 返回的窗口就是500K;
  • 但实际上可能处理端处理的速度很快, 10ms之内就把500K数据从缓冲区消费掉了
  • 在这种情况下, 接收端处理还远没有达到自己的极限, 即使窗口再放大一些, 也能处理过来
  • 如果接收端稍微等一会再应答, 比如等待200ms再应答, 那么这个时候返回的窗口大小就是1M;
  • , 窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽量提高传输效率
  • 数量限制: 每隔N个包就应答一次;
  • 时间限制: 超过最大延迟时间就应答一次;
  • 具体的数量和超时时间, 依操作系统不同也有差异; 一般N取2, 超时时间取200ms;

捎带应答

在延迟应答的基础上, 我们发现, 很多情况下, 客户端服务器在应用层也是 "一发一收" 的. 意味着客户端给服务器说
了 "How are you", 服务器也会给客户端回一个 "Fine, thank you";
那么这个时候ACK就可以搭顺风车, 和服务器回应的 "Fine, thank you" 一起回给客户端