TCP 使用 确认应答 策略,对每一个发送的数据段,都要给一个 ACK 确认应答。收到 ACK 后再发送下一个数据段。这样做有一个比较大的缺点,就是性能较差。尤其是数据往返的时间较长的时候。

既然这样一发一收的方式性能较低,那么我们一次发送多条数据,使用滑动窗口,就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了)。
滑动窗口存在的意义就是在保证可靠性的前提下,尽量提高效率。


那么如果出现了丢包,如何进行重传?这里分两种情况讨论。

这种情况下,部分 ACK 丢了并不要紧,因为可以通过后续的ACK进行确认.
ACK 的确认号有特定含义,保证后一条 ACK 覆盖前一条,
比如并没有收到 1001 ACK, 但是收到了 2001 ACK 就说明 2001 之前的数据全部已经收到了。
若发送 4001 ~ 5000 之前,只收到了 4001, 但是它的意思是,4001 之前的都收到了,窗口就可以一次往下挪动 4 个。

这种机制被称为 “高速重发控制”(也叫 “快重传”)。
(为什么说是 “快” 重传,因为可能收到三个连续相同的 ACK 的时间内还没有触发超时机制,也就是还没超时呢,但是不等触发超时,直接就重传了。)
流量控制是滑动窗口的延伸,目的是为了保证可靠性。
因此 TCP 支持根据接收端的处理能力,来决定发送端的发送速度。这个机制就叫做流量控制(Flow Control);

接收端如何把窗口大小告诉发送端呢?
回忆我们的 TCP 首部中,有一个16 位窗口字段,就是存放了窗口大小信息;
那么问题来了,16 位数字最大表示 65535,那么 TCP 窗口最大就是 65535 字节么?
实际上,TCP 首部40字节选项中还包含了一个窗口扩大因子 M,实际窗口大小是 窗口字段的值左移 M 位即 65535 * 2 ^M
拥塞控制,也是滑动窗口的延伸,限制滑动窗口的发送速率。
拥塞控制描述的是发送方到接收方整个链路直接的拥堵情况。
虽然TCP有了滑动窗口这个大杀器,能够高效可靠的发送大量的数据。
但是如果在刚开始阶段就发送大量的数据,仍然可能引发问题。
因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵。在不清楚当前网络状态下,贸然发送大量的数据,是很有可能引起雪上加霜的。
具体拥塞窗口是怎么变化的呢 ?

当TCP通信开始后,网络吞吐量会逐渐上升;随着网络发生拥堵,吞吐量会立刻下降;
为什么使用指数级别的增长速度 ?
因为希望能快速接近 ssthresh 阈值, 既希望速度快,又希望不大量丢包,如果初始情况给的窗口大小很小,可能合适的值是个很大的值,那么使用指数增长的话,能够很快的接近这个值。
ssthresh 的意义 ?
决定了什么时候从指数增长变为线性增长
拥塞窗口最理想的大小 ?
ssthresh 值与 出现拥塞的这个值之间是最理想的效果,这个范围之间传输速率较快,并且没有大量丢包。
为什么出现拥塞时,直接让窗口变为初始值 1 ?
因为网络的情况很复杂,不稳定,如果出现大量丢包,很可能速度降下来一点是不能解决问题的,速度降得太慢还会有可能出现持续丢包,就会对网络质量带来很大影响,一下让窗口变得很小,就是期望这次传输一定能成功。
拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的折中方案。
常见面试题:
以上两个问题答案类似,都可以参考TCP的可靠性机制然后在应用层实现类似的逻辑:
如:
好啦! 以上就是对 TCP 滑动窗口、流量控制 以及拥塞控制的讲解,希望能帮到你 !
评论区欢迎指正 !