第七章 拥塞控制与流控制

发送窗口

无论是遇到网络拥塞或者接收方缓存已满的情况,都需要降低发送端的发包速率,当网络或接收方缓存恢复后,需要能够快速恢复发包速率。因此,发送端需要一种调节发包速率的机制,在 UDPack 协议中,采用了类似 TCP 协议的发送窗口机制。

发送端每隔一段时间会周期处理发送缓存中的数据包,发送窗口定义为每个周期内发送的数据包个数。开始时,第 1 次发送 1 个,第 2 次发送 2 个,第 3 次发送 4 个,以指数快速增加发送窗口大小。当发送窗口到阀值后,可以继续采用线性缓慢增加发送窗口大小。通过增加每个周期内数据包发送个数,提升了发包速率。

但是,发包速率不可能无限增长下去,发送窗口大小也不能一直增大。事实上,如果产生数据包的速率很高,发送窗口会持续增加,一段时间后很可能会遇到网络拥塞,数据包重试次数升高。或者也可能会遇到接收端来不及处理,接收缓存满的情况。此时,需要重置发送窗口大小,快速降低发包速率。发送窗口被重置后,会再次先以指数,然后以线性速度恢复。

每个 Session 有自己的独立的发送窗口,独立进行拥塞控制和流控制。

拥塞控制

众多周知,网络情况处于实时动态变化之中,且无法预测。遇到网络好的时候,可以获得较高的传输速度。遇到网络拥塞的时候,延时上升,丢包率上升,传输速度会降低很多。互联网上所有正在运行的应用程序为了最大化利用网络传输数据,都会尝试更高的发包速率。网络高峰时期,网络拥塞是不可避免的。此时路由中的中间设备会对往来数据包进行排队处理,造成延时上升,甚至直接丢弃处理,造成丢包率上升。而且中间设备在处理数据包时,根据协议的不同也可能采取不同的策略。一般情况,所有中间设备会更重视 TCP 协议数据包,毕竟 TCP 是当前互联网上应用最广泛的传输层协议。

遇到网络拥塞时,互联网上的所有应用程序都应该主动降低发包速率,使得网络拥塞得以缓解和恢复。如果此时仍然采取较高的发包速率,会加剧拥塞状况,延时和丢包率异常高,应用程序不断尝试重发数据包,造成网络传输效率及其低下。当前已有很多著名的拥塞控制算法可以识别拥塞并有效调节发包速率。但是在 UDPack 协议中并没有使用它们,而是设计了一个更简单和容易实现的拥塞控制算法,是否有效有待实践检验。

该算法的核心指标是 AvgRetryRatio,也就是发送缓存中的所有正在发送中的 Packet 的平均重试比率。例如,发送缓存中有 p1, p2, p3 个数据包,重试次数分别为 0, 1, 0,则 AvgRetryRatio = 0.33 (1/3)。分子为总的重试次数 1,分母为总的数据包个数 3。AvgRetryRatio 可以反映出当前网络状况下的数据包重试水平。AvgRetryRatio 越趋近于 0 代表网络状况越好,越大则代表网络越差,出现了更多的重试。

可以设置一个合理的阀值,当 AvgRetryRatio 超过阀值时,则认为发送了拥塞,此时可通过重置发送窗口大小来降低发包速率。

流控制

发送端发送速率和接收端接收速率不一定匹配,发送速率过低则可能没有有效利用网络,数据传输慢。发送速率过高,一方面占用了更多了网络资源,另一方面接收端可能来不及处理,装满缓冲区。所以需要一种流控制机制,可以根据实际情况控制发送端的发送速率。

发送端为每个 Session 设置发送缓冲区,如果 Packet 产生过快造成缓冲区满,则直接拒绝发送新的 Packet。通过把发送缓冲区的设置小一些,使得发送端必须降低发包速率,否则会被拒绝。

接收端可以为每个 Session 设置接收缓冲区,如果接收缓冲区满,可通过 ACK 帧设置 SLOW 标记,通知发送端降低发送速率,发送端收到 ACK 帧,读取 SLOW 标记,并通过重置发送窗口大小来降低发送速度。

计算 Session 的发送或者接收缓冲区大小时,需合并计算 Session 本身的缓冲区及 Session 之上打开的所有 Stream 的缓冲区。