这是通信领域的话题了,低延迟传输有上百种优化方式,上面说的那些冗余码只是很小一部分,不考虑信道容量的冗余编码系统都是在耍流氓,不用等到同信道内跑两套这样的协议你才会发现问题,一套协议再接近信道带宽容量限制时,就会出现指数上升的丢包率,所以不考虑带宽检测的冗余法就是一个残次品。
要系统的解决低延迟传输问题,需要同时在传输层,协议层,路由层,应用层几个方面着手:
传输层带外冗余:弱智重复法
设你要发送的数据为 x1-xn,你实际发送出去的包为 p1-pn,那么比如 Pn = [Xn, Xn-1, Xn-2],重复前面出现过的 1-2个数据,丢包了你可以随时恢复出来。
传输层带外冗余:异或法
每发四个包 x1-x4,你多发一个冗余包,内容为前面四个包的异或: R = x1 ^ x2 ^ x3 ^ x4,那么本组数据五个数据包(x1-x4, R)中任意丢失一个,都可以从其他四个异或得到。当然,不一定要四个包冗余一个,你可以根据情况和丢包率,两个包或者三个包冗余一个。
传输层带外冗余:解方程法
把每个数据包看成一个整数,要发送 x1-x4 四个包,并不直接发送x,而是将他们进行线性运算得到 y1-y7,然后发送出去:
A1 * x1 + B1 * x2 + C1 * x3 + D1 * x4 = y1 收到 A2 * x1 + B2 * x2 + C2 * x3 + D2 * x4 = y2 收到 A3 * x1 + B3 * x2 + C3 * x3 + D3 * x4 = y3 丢失 A4 * x1 + B4 * x2 + C4 * x3 + D4 * x4 = y4 收到 A5 * x1 + B5 * x2 + C5 * x3 + D5 * x4 = y5 丢失 A6 * x1 + B6 * x2 + C6 * x3 + D6 * x4 = y6 收到 A7 * x1 + B7 * x2 + C7 * x3 + D7 * x4 = y7 丢失
接收方收到一组数据以后,发现y3, y5, y7丢失,得到:
A1 * x1 + B1 * x2 + C1 * x3 + D1 * x4 = y1 A2 * x1 + B2 * x2 + C2 * x3 + D2 * x4 = y2 A4 * x1 + B4 * x2 + C4 * x3 + D4 * x4 = y4 A6 * x1 + B6 * x2 + C6 * x3 + D6 * x4 = y6
如果y1-y7在发送的过程中丢失了三个:y3, y5, y7 观察等式。变量任然有4个,参数也任然有四个A1, A2, A4, A6, B?, C?, D? 四个变量,四个等式,那么我们可以通过解方程求出x1-x4,这就是一个矩阵运算和求逆的过程,也就是楼上说的 Read-Solomon 冗余码的基本原理(PS: shorthair/long hair两个库写的很烂呀),实际传输时需要根据网络质量来选择每组数据和冗余包的比例。
传输层网络评估
需要一套比较强大的系统,实时评估当前网络质量(RTT, 丢包率,抖动值,可用带宽),为协议决策提供参考,比如这时一个带宽很大,延迟却很高的信道呢?还是带宽很小,延迟也很小的信道呢?不同的情况对应不同的策略,当前丢包是常规丢包?震荡性丢包?还是接近信道限制出现无可挽回的丢包?再根据当前协议出于什么情况?交互模式还是单向传输模式,来给出最佳的传输策略。不考虑这些情况的协议,都是比较弱智的(比如楼上提到的几种)。
协议层决策模型
根据不同的情况,来推导不同的数据包丢失后,对整体协议的影响,从而通过马科夫决策过程,来找到哪个包丢失的代价最大,以此来指导冗余包的生成过程:
并且根据网络统计,实时修正决策参数。
协议层响应模型
对 TCP 协议而言,是一个中庸的协议,协议质量的几个要素:公平性,延迟性,带宽利用率。三者不可得兼,然而可以根据你应用情况进行适当调整,牺牲部分公平性和降低部分带宽利用率换取低延迟,比如:
协议层优化结合前面的冗余和信道质量评估,跳出 TCP 的限制,以牺牲部分公平性和降低部分带宽利用率的代价,可以得到更低的传输延迟。
这部分我开源了,欢迎访问:skywind3000/kcp
路由层优化:路由实时评估
公网提供的路由并不是最佳的,书上虽然说路由就是要找最优通路,实际情况是,最优通道价格高,屌丝用户就走走免费的省道国道得了,高富帅再走高速。因此你需要建立自己的全局路由系统。对全局网络有一个比较明确的评估。
路由层优化:路由动态选择
根据前面的实时路由评估结果,使用空闲用户帮忙转发以及 IDC 布点的模式,决策最佳的传输路径,并且考虑线路冗余(同一个时刻保持两条以上的通路,一条挂了实时切换到另外一条)。每两三个路由节点之间,采用三角连接法,每个节点至少保证两条线路:
不要以为网络上地理位置越近的传输速度越快,也不要以为跳数越少传输速度越快,实际情况是,正确的选择路由,即便跳数增加,也能极大的提升传输速度。
物理层传输优化
是用更好的传输介质(比如光纤),或者增加传输功率,又或者再功率不变的情况下使用低频信号(但是低频信道带宽一般不高),都能降低信号损失。一般来讲,可以通过评估信号衰减来自动增加减少功率,实时切换使用高低频信道。或者更换天线或者信号源位置让衰减最低(好像有点扯远了)。。。
使用吞吐量更大的设备,同样成本下一般带宽,每秒发包数,RTT三者不可得兼,根据情况选择擅长 RTT 或者带宽的设备。
应用层带内冗余:低频数据冗余
比如你在传送一段音频的话,比如每秒 40 个数据帧,用类似 MP3 的压缩方法,那么你可以将每秒的音频分作 40 段,每段 25ms,然后进行 DCT ,滤波,量化,无损压缩等几个传统步奏后,得到一段类似 mp3 的25ms的数据,我们叫一帧,发包的时候,除了包含这一帧的数据外,还同时包含上几帧的低频数据,这样对带宽增加不大,然而我们再解码的时候,如果发现丢包就可以用其他包含有的本帧低频数据,还原出一个质量没有原来好,但是听起来差不多的数据,这叫低频数据冗余。
应用层带内冗余:DSP处理
这是更高级的处理方式,比如你在传输视频,视频中最费带宽的是 I 帧数据,即便 H.265,I帧数据比较其他帧也是很大的。然而 H.264, H.265 系列并没有很好的考虑容错和抗损坏。我们可以用帮他们在这方面做的更好一些,才用频域交错存储及其他一些方法,比如把一幅质量为 70% 大小为 32KB 的 I 帧图像,存储成两张质量为 40% 大小为 16KB - 18KB 的 I 帧图像,两个拆分后的图像单独看,都是一副质量更低的图片,但是他们组合起来,却能变成一张质量很好的图片。这样对于传输视频而言,I帧的瓶颈就不那么明显了,即时丢了一半,也只是损失一下效果而已。
修改 H.264/ H.265的方法有很多,上面只是举个例子。如果你传送的是视频数据的话,你可以找一找相关的文章,教你改进 H.264, H.265。同样如果你传 AAC的话,也有很多方法在带内实现冗余。
上面说的各种带外冗余主要是要 “增加带宽”, 而应用层的 “带内冗余” 主要思路是丢包了就降低质量,在带宽收到限制的时候,我们往往需要果断的使用各种带内冗余法,来保证较流畅的传输体验。
应用层误差掩盖
传送视频数据发现局部丢失了,可以用前后两帧画面以及周围的图块来推断丢失的图块可能是什么,然后模拟生成一下,要比直接马赛克好很多。
传送音频数据发现局部丢失了,可以用前后音频频域插值模拟出丢失的声音片段,也可以通过学习的方式,通过前面历史数据的基音和共振峰提取,来模拟丢失的声音。
即便不模拟,还有方法是通过生成一段舒适的背景噪音来填充丢失的片段,这样会比直接空白音好很多,或者通过不改变基音却变慢语速的方式,让前后两段声音接起来,这样你听着就是声音慢了几十毫秒,但是很连续,发现不了其中丢失。
比如你传送游戏数据的话,一般位移数据可以用非可靠传输方式来传输,每组移动数据不但包含坐标和方向,还要包括时间和速度,这样,数据丢失了,你从后面的数据恢复出来以后,画面上做一个移动差值(导航推测),立马就能掩盖这次传输误差。
所谓应用层误差掩盖,是根据你的应用特点来设计规则,即便传输数据丢失,你也能通过各种模拟和插值的方式掩盖过去,只要保证后来的数据能够修正即可,这样即便信号不好,出现短时误差了,也能得到即时的纠正。
应用层延迟控制
数据包前后顺序依赖(Pn依赖 Pn-1)可以进化为交错依赖(Pn依赖 Pn-2,Pn+1 依赖 Pn-1),这样丢包发生只会导致一部分后续数据无法工作,而不会导致某一时间内所有后续数据没法工作。
控制缓存策略,缓存小了延迟低但是效率也低,缓存大了延迟高,效率也高。网络拥塞了缓存怎么办?使用更少的往返次数来实现交流。
。。。。。。
总结
所谓 “降低延迟”,其实就是 “提高通信质量”,这个问题通信行业一直在研究解决的事情,不可能靠单方面的技巧提高很多(比如冗余),这是一个系统工程,有它的理论体系,有它的实践方法,还有各种注意事项。
----