所谓的弱网络环境就是网络不是很好,比如无线wifi,跨多层网络路由、或者网路负载过大等等情况。这样数据在传输种会发生丢失的情况。
再说,网络协议,传输层分udp和tcp.本人对tcp做了专门的分析。
看图,我只开了开了2%的丢包,导致发送速度从>500Mb/s下降到 <160Mb/s。速度下降比率达70-80%,发生这个现象的原因是TCP的拥塞处理,当发现网络丢包的时候,就启动拥塞控制机制,降低发送速度,达到tcp可靠性传输的目的。大于大数据和视频的传输,速度在传输成降低这么多,很难满足应用层的需求,如果是视频就会导致视频的卡顿。因为接收端收不到需要的视频帧,只有停下来花更多的时间等待。实时上笔者在实际开发视频传输的时候使用过tcp,在我无线网络的情况下,tcp确实特别容易卡顿。关于tcp拥塞控制,大家可以搜索查阅相关的资料,这个不做细说。
笔者用的丢包工具是clumsy0.2。
我们查明了tcp卡顿的原因后,决定放弃tcp,那只有udp了,但是udp又不可靠,会有丢包和乱序的问题,导致视频播放的花屏,这个花屏是因为关键帧或者参考帧丢失了,导致后界的视频无法正常的恢复,从而出现花屏。于是有查阅了相关的资料,找到了一个udt 协议。
关于udt协议,网上的说明如下:
基于UDP的数据传输协议(UDP-based Data Transfer Protocol,简称UDT)是一种互联网数据传输协议。UDT的主要目的是支持高速广域网上的海量数据传输,而互联网上的标准数据传输协议TCP在高带宽长距离网络上性能很差。顾名思义,UDT建于UDP之上,并引入新的拥塞控制和数据可靠性控制机制。UDT是面向连接的双向的应用层协议。它同时支持可靠的数据流传输和部分可靠的数据报传输。 由于UDT完全在UDP上实现,它也可以应用在除了高速数据传输之外的其它应用领域,例如点到点技术(P2P),防火墙穿透,多媒体数据传输等等。
按照这个及时他是基于udp的协议,又是可靠的,那么是否不会有tcp丢包拥塞的问题。
于是对udt协议做了测试,利用其项目代码里面的demo测试。
udp是开源的项目,下载地址是:https://sourceforge.net/projects/udt/。目前是udt4.
如下图是没有丢包干扰的情况下的发送截图:
开了丢包干扰又会如何呢,
我们看到udt 协议也是一样的开了丢包干扰后速度下降更多。那为什么呢,不是说udt是基于udp的协议,而udp不会发生拥塞控制么?为此笔者阅读了udt的代码,发现他就是在udp的基础上做加了应答处理,让udp变得可靠,当然也有“拥塞控制算法”,只不过是应用层的,笔者认为由于这个控制算法是基于应用层的,而且发展的时间,使用的频率都没有tcp长,所有在发生拥塞的时候的表现自然要弱于tcp很多。
那么笔者又想到一个问题,那为什么要有udt协议呢?直接使用tcp不是挺好么?笔者又查阅了资料,其实在此文关于udt介绍里面就有答案:由于UDT完全在UDP上实现,它也可以应用在除了高速数据传输之外的其它应用领域,例如点到点技术(P2P),防火墙穿透,多媒体数据传输等等。udt就是为了做p2p*容易 和 传输可靠而已。
至此,关于本文标题的答案探索,似乎走向了死角,所有的探索都失败了。在笔者不屑的努力下,终于又柳暗花明。
笔者查到了webrtc项目关于抗丢包处理策略,简称为QOS-FEC-NACK技术,FEC 中文意思是向前纠错。NACK 是选择性重传,QOS 是处理乱序问题。基于这样一整套在应用层的丢错重传机制,最大程度了保证了视频的传输的可靠性。下面笔者分别来介绍这个技术。
首先介绍FEC(Forward Error Correction),FEC算法的原理,参见这个博客https://blog.csdn.net/u010178611/article/details/82656838。这里贴出其博客的全文。
笔者经过半年的技术攻关开发出了QOS-NACK-FEC抗丢包传输协议,可以加群了解。
阅读了FEC的纠错原理后,笔者有了疑问:FEC生成冗余包大小跟原始包是一样的,那么实际上是增加了传输的数据量,发生丢包的时候,为什么还要增加数据量。笔者做了一个计算。加入一个视频帧有100个包,生成了10个冗余包,传输的时候会丢到9个包,那么视频还会恢复。但是如果没有这10个冗余包,即使只丢一个包视频也无法恢复。这就是FEC的好处,在一定数量的丢包的情况下,可以快速的恢复视频,因为不用请求重传,所以恢复速度快。只有当丢失过多,比如丢失了11个包,无法恢复的时候,需要请求重传。
NACK是丢包请求重传,下面介绍一下其实现原理,和系统框架设计。
NACK可以放置在原来的FEC-QOS传输层之外,作为上层应用层,这种实现方式NACK将FEC-QOS看做普通的UDP传输,二者并无紧密结合,其优势是可以与成熟的NACK方案无缝衔接。我们知道任何NACK方案都必将引入延时抖动,因为接收端在发起重传请求后,需要等待发送端重新发出的数据,在“重传等待时间”内不对外输出数据。而QOS阶段里为解决UDP乱序包的问题也引入了一个“丢包等待时间”,当遇到包序号不连续时,将等待这一时间,若仍未收到所需的包则认定丢包,不再等待。如果将这两个时间合二为一,可以尽量的降低系统时延和抖动,毕竟我们需要的是一个高实时性的NACK传输方案。我们将NACK的发起和等待放置在QOS之中,入下图所示:
图1 在QOS中实现NACK发起
当QOS检测到序号不连续时,可能是发生丢包或者是乱序,此时QOS将通过FEC解码模块分析当前疑似丢包是否将导致FEC无法恢复。此时将产生三种分析结果:
A、当前丢包即使丢了也不影响FEC恢复,比如当前丢失的包为一个或者多个冗余包,且该冗余包所在的group内的媒体包均已接收,或者借助已接收的冗余包足够恢复。
B、当前丢包不能确定是否影响FEC恢复,需要接收更多的包才能确定。比如丢包发生在group的中段且丢的数量小于冗余包总数。
C、当前丢包将导致FEC确定无法恢复,比如同一个group内丢失的包数大于冗余包总数。
对于情况A,QOS将直接不予等待,将后续接收的包直接交与FEC。对于情况B,QOS将进入“丢包等待时间”,以期收到乱序的包。对于情况C,QOS将发起NACK重传并进入等待,这个等待时间即是“丢包等待时间”又是“重传等待时间”,在等待期内不管是该乱序包到达或者重传包到达,都能满足FEC的恢复条件。
在介绍了NACK的发起条件后,我们来关注“重传等待时间”的取值问题。若设置固定的重传等待时间将很难满足各类网络情况。时间过小将导致重传包尚未到达,QOS已结束等待并输出后续包,后续即便再收到重传包也将直接丢弃。重传包也可能因网络原因丢包,若“重传等待时间”过大,将导致更大的延时和抖动。为了提高自适应能力,系统通过实时获取当前网络的UDP通讯RTT时间来作为“重传等待时间”的参考,计算出合理的值。
三、信令通道与媒体通道分离
我们使用独立的一个UDP信令通道用来传输NACK请求,而不是复用媒体通道。这样做的主要考虑是:
A、媒体通道上使用的FEC\QOS将必然引入部分延时和抖动(具体参见FEC\QOS原理说明),NACK请求对于时间特别敏感,希望是越早越快通知对方越好。在信令通道上我们将只进行裸UDP收发,不会加入FEC和QOS。
B、避免出现NACK请求包丢失后也发起NACK重传请求的情况。
C、更好的兼容性,对于不支持NACK的节点,只需要忽略信令通道的内容即可与NACK节点互通。
D、程序实现上更加简洁,无需在媒体通道上新增包类型来区分哪个包是NACK请求包。媒体包上增加字节也都会直接转化为带宽的增长。
四、实现流程
方案的实现流程如下图所示:
图2 NACK的整体流程
对于传输层模块,它是全双工的,为演示方便我们只列出单向的情况,另外一个方向也是完全一致的。在FEC编码之后,所有的发出的UDP(RTP)包均会被存入一个环形缓存区中,当收到远端NACK请求时,将在下一个媒体包传输时触发重传动作,后者将在环形缓存区中检索需要重传的包并在媒体通道上发出。我们没有新增内部线程去执行重传动作,而是借助原本的媒体包发送行为来触发,这样可以简化设计提高稳定性。
检索过程中我们进行了数据包的合法性校验和时间戳有效性校验,当发现当前时间距离数据包初次发送时间的间隔已经较大时,将放弃重传,因为此时远端极有可能已经退出等待,没有必要再浪费带宽。检索使用的是RTP头中的序号字段,需要考虑序号达到最大值时的跳变动作。
有意交流的可以私信留言 微信:Lovexx201709