网络拥塞是基于IP协议的数据报交换网络中常见的一种网络传输问题,它对网络传输的质量有严重的影响,网络拥塞是致使网络吞吐下降,网络丢包等的主要缘由之一,这些问题使得上层应用没法有效的利用网络带宽得到高质量的网络传输效果。特别是在通讯领域,网络拥塞致使的丢包,延迟,抖动等问题,严重的影响了通讯质量,若是不能很好的解决这些问题,一个通讯产品就没法在现实环境中正常使用。在这方面WebRTC中的网络拥塞控制算法给咱们提供了一个可供参考的实现,本篇文章会尽可能详细的介绍WebRTC中的拥塞控制算法---GCC的实现方式。
相关阅读推荐web
《聊聊WebRTC网关服务器1:如何选择服务端端口方案》算法
《聊聊WebRTC网关服务器2:如何选择PeerConnection方案?》浏览器
WebRTC是一个Web端的实时通讯解决方案,它能够作到在不借助外部插件的状况下,在浏览器中实现点对点的实时通讯。WebRTC已经由W3C和IETF标准化,最先推出和支持这项技术的浏览器是Chrome, 其余主流浏览器也正在陆续支持。Chrome中集成的WebRTC代码已所有开源,同时Chrome提供了一套LibWebRTC的代码库,使得这套RTC架构能够移植到其余APP当中,提供实时通讯功能。服务器
本文主要介绍的是WebRTC的拥塞控制算法,WebRTC的传输层是基于UDP协议,在此之上,使用的是标准的RTP/RTCP协议封装媒体流。RTP/RTCP自己提供不少机制来保证传输的可靠性,好比RR/SR, NACK,PLI,FIR, FEC,REMB等,同时WebRTC还扩展了RTP/RTCP协议,来提供一些额外的保障,好比Transport-CCFeedback, RTP Transport-wide-cc extension,RTP abs-sendtime extension等,其中一些后文会详细介绍。网络
GCC算法主要分红两个部分,一个是基于丢包的拥塞控制,一个是基于延迟的拥塞控制。在早期的实现当中,这两个拥塞控制算法分别是在发送端和接收端实现的,接收端的拥塞控制算法所计算出的估计带宽,会经过RTCP的remb反馈到发送端,发送端综合两个控制算法的结果获得一个最终的发送码率,并以此码率发送数据包。下图即是展示的该种实现方式:session
从图中能够看到,Loss-Based Controller在发送端负责基于丢包的拥塞控制,它的输入比较简单,只须要根据从接收端反馈的丢包率,就能够作带宽估算;上图右侧比较复杂,作的是基于延迟的带宽估计,这也是本文后面主要介绍的部分。在最近的WebRTC实现中,GCC把它的两种拥塞控制算法都移到了发送端来实现,可是两种算法自己并无改变,只是在发送端须要计算延迟,于是须要一些额外的feedback信息,为此WebRTC扩展了RTCP协议,其中最主要的是增长了Transport-CC Feedback,该包携带了接收端接收到的每一个媒体包的到达时间。架构
基于延迟的拥塞控制比较复杂,WebRTC使用延迟梯度来判断网络的拥塞程度,延迟梯段的概念后文会详细介绍;ide
其算法分为几个部分:测试
在得到两个拥塞控制算法分别结算到的发送码率以后,GCC最终的发送码率取的是两种算法的最小值。下面咱们详细介绍WebRTC的拥塞控制算法GCC。google
基于丢包的拥塞控制比较简单,其基本思想是根据丢包的多少来判断网络的拥塞程度,丢包越多则认为网络越拥塞,那么咱们就要下降发送速率来缓解网络拥塞;若是没有丢包,这说明网络情况很好,这时候就能够提升发送码率,向上探测是否有更多的带宽可用。实现该算法有两点:一是得到接收端的丢包率,一是肯定下降码率和提高码率的阈值。
WebRTC经过RTCP协议的Receive Report反馈包来获取接收端的丢包率。Receive Report包中有一个lost fraction字段,包含了接收端的丢包率,以下图所示。
另外,WebRTC经过如下公式来估算发送码率,式中 As(tk) 即为 tk 时刻的带宽估计值,fl(tk)即为 tk 时刻的丢包率:
简单来讲,当丢包率大于10%时则认为网络有拥塞,此时根据丢包率下降带宽,丢包率越高带宽降的越多;当丢包率小于2%时,则认为网络情况很好,此时向上提升5%的带宽以探测是否有更多带宽可用;2%到10%之间的丢包率,则会保持当前码率不变,这样能够避免一些网络固有的丢包被错判为网络拥塞而致使下降码率,而这部分的丢包则须要经过其余的如NACK或FEC等手段来恢复。
WebRTC实现的基于延迟梯度的带宽估计有两种版本:
关于如何根据延迟梯度推断当前网络情况, 后面会分几点详细展开讲, 整体来讲分为如下几个步骤:
其过程就是,到达时间滤波器根据包间的到达时延和发送间隔,计算出延迟变化,这里会用到卡尔曼滤波对延迟变化作平滑以消除网络噪音带来的偏差;延迟变化会做为过载检测器的输入,由过载检测器判断当前网络的状态,有三种网络状态返回overuse/underuse/normal,检测的依据是比较延迟变化和一个阈值,其中该阈值很是关键且是动态调整的。最后根据网络状态的变化,速率控制器根据一个带宽估计公式计算带宽估计值。
前面屡次提到WebRTC使用延迟梯度来判断网络拥塞情况,那什么是延迟梯度,为何延迟梯度能够做为判断网络拥塞的依据,咱们在这里详细介绍,首先来看如下,延迟梯度是怎样计算出来的:
1. 延迟梯度的计算
如上图所示,用两个数据包的到达时间间隔减去他们的发送时间间隔,就能够获得一个延迟的变化,这里咱们称这个延迟的变化为单向延迟梯度(one way delay gradient),其公式可记为:
那么为何延迟梯度能够用来判断网络拥塞的呢,以下面两图所示:
左边这幅图的场景是理想情况下的网络传输,没有任何拥塞,按咱们上面提到的公式(2)来计算,这种场景下,所计算到的延迟梯度应该为0。而右边这幅图的场景则是发送拥塞时的情况,当包在t2时刻到达时,该报在网络中经历过一次因拥塞致使的排队,这致使他的到达时间比本来要完,此时计算出的延迟梯度就为一个较大的值,经过这个值,咱们就能判断当前网络正处在拥塞状态。
在WebRTC的具体实现中,还有一些细节来保证延迟梯度计算的准确性,总结以下:
2) 到达时间间隔小于5ms的数据包被归为一组,这是因为在wifi网络下,某些wifi设备的转发模式是,在某个固定时间片内才有机会转发数据包,这个时间片的间隔可能长达100ms,形成的结果是100ms的数据包堆积,并在发送时造成burst,这个busrt内的全部数据包就会被视为一组。
2. transport-cc-feedback消息
具体消息格式以下:
如上图所示,红框以前的字段是RTCP包的通用字段,红框中的字段为transport-cc的具体内容,其中前四个字段分别表示:
在此以后,是两类信息:多个packet chunk字段和多个recv delta字段。其中pcaket chunk具体含义以下:
以下两图所示, 表示媒体包到达状态的结构有两种编码方式, 其中 T 表示chunk type;0表示RunLength Chunk, 1表示Status Vector Chunk.
1)Run LengthChunk
这种表示方式是用于,当咱们连续收到多个数据包,他们都有相同的到达状态,就能够用这种编码方式。其中S表示的是到达状态,Run Length表示有多少个连续的包属于这一到达状态。
到达状态有三种:
00 Packet not received
01 Packet received, small delta (所谓small detal是指能用一个字节表示的数值)
10 Packet received, large ornegative delta (large便是能用两个字节表示的数值)
2) Status Vector Chunk
这种表示方式用于每一个数据包都须要本身的状态表示码,固然仍是上面提到的那三种状态。可是这里的S就不是上面的意思,这里的S指的是symbol list的编码方式,s = 0时,表示symbollist的每个bit能表示一个数据包的到达状态,s = 1时表示每两个bit表示一个数据包的状态。
s = 0 时
0 Packet not received
1 Packet received , small detal
s = 1 时
同 Run Length Chunk
最后,对于每个状态为Packet received 的数据包的延迟依次填入|recv delta|字段,到达状态为1的,recv delta占用一个字节,到达状态为2的,recv delta占用两个字节能够看出以上编码的目的是为了尽可能减小该数据包的大小,由于每一个媒体包都须要反馈他的接受状态。
到达时间滤波器计算出每组数据包的延迟梯度以后,就要据此判断当前的网络拥塞状态,经过和某个阈值的比较,高过某个阈值就认为时网络拥塞,低于某个阈值就认为网路状态良好,所以如何肯定阈值就相当重要。这就是过载检测器的主要工做,它主要有两部分,一部分是肯定阈值的大小,另外一部分就是依据延迟梯度和阈值的判断,估计出当前的网络状态,一共有三种网络状态: overuse underuse normal,咱们先看网络状态的判断。
1. 网络状态判断
判断依据入下图所示:
其中表示的是计算出的延迟梯,
表示的是一个判断阈值,这个阈值是自适应的, 后面还会介绍他是怎么动态调整的,这里先只看如何根据这两个值判断当前网络状态。
从上图能够看出,这里的判断方法是:
这样计算的依据是,网络发生拥塞时,数据包会在中间网络设备中排队等待转发,这会形成延迟梯度的增加,当网络流量回落时,网络设备快速消耗(转发)其发送队列中的数据包,然后续的包排队时间更短,这时延迟梯度减少或为负值。
这里了须要说明的是:
2. 自适应阈值
上节提到的阈值值,它是判断当前网络情况的依据,因此如何肯定它的值也就很是重要了。虽然理想情况下,网络的延迟梯度是0,可是实际的网络中,不一样转发路径其延迟梯度仍是有波动的,波动的大小也是不同的,这就致使若是设置固定的
太大可能没法探测到拥塞,过小又太敏感,致使速率了变化很大。同时,另一个问题是,实验中显示固定的值会致使在和TCP连接的竞争中,本身被饿死的现象(TCP是基于丢包的拥塞控制),所以WebRTC使用了一种自适应的阈值调节算法,具体以下:
(1) 自适应算法
上面的公式就是GCC提出的阈值自适应算法,其中:
每组数据包会触发一次探测,同时更新一次阈值,这就是距上次更新阈值时的时间间隔。
是一个变化率,或者叫增加率,固然也有多是负增加,增加的基值是:当前的延迟梯度和上一个阈值的差值---
。其具体的取值以下:
其中:ku = 0.01; kd = 0.00018
从这个式子中能够看出,当延迟梯度减少时,阈值会以一个更慢的速率减少; 延迟梯度增长时,阈值也会以一个更慢的速度增长;不过相对而言,阈值的减少速度要小于增长速度。
速率控制器主要实现了一个状态机的变迁,并根据当前状态来计算当前的可用码率,状态机以下图所示:
速率控制器根据过载探测器输出的信号(overuse underusenormal)驱动速率控制状态机, 从而估算出当前的网络速率。从上图能够看出,当网络拥塞时,会收到overuse信号,状态机进入“decrease”状态,发送速率下降;当网络中排队的数据包被快速释放时,会受到underuse信号,状态机进入“hold”状态。网络平稳时,收到normal信号,状态机进入“increase”状态,开始探测是否能够增长发送速率。
在Google的paper[3]中,计算带宽的公式以下:
其中= 1.05,
=0.85。从该式中能够看到,当须要Increase时,之前一次的估算码率乘以1.05做为当前码率;当须要Decrease时,以当前估算的接受端码率(Rr(ti))乘以0.85做为当前码率;Hold状态不改变码率。
最后,将基于丢包的码率估计值和基于延迟的码率估计值做比较,其中最小的码率估价值将做为最终的发送码率。
以上即是WebRTC中的拥塞控制算法的主要内容,其算法也一直还在演进当中,每一个版本都有会有一些改进加入。其余还有一些主题这里没有覆盖到,好比平滑发送,能够避免突发流量; padding包等用来探测带宽的策略。应该说WebRTC的这套机制能覆盖大部分的网络场景,可是从咱们测试来看有一些特殊场景,好比抖动或者丢包比较高的状况下,其带宽利用率仍是不够理想,但整体来讲效果仍是很不错的。
另外,想要获取更多产品干货、技术干货,记得关注网易云信博客。