文章目录
与TCP数据传输机制不同,采用UDP向目标地址发送报文时。从理论上说,数据的接收顺序有可能是乱序的,更令人头痛的是UDP其发出后不管的特性使到数据是否已经到达无法感知,而丢包问题又经常发生,所以采用UDP进行数据传输时,基本上一直围绕着丢包处理这件事展开。
那么当丢包事情发生的时个,一般的处理办法如下:
-
NACK机制
当接收端发现数据包丢失时,通过向发送端发送丢包重发请求或者是数据包接收状态,对数据数据丢包与接收情况向发送端发起反馈(具体的实现逻辑有很多种,有超时重发,丢包请求重发等,具体方案可按项目要求进行实现),驱使发送端对某个接收端的丢失数据进行定点发送。 -
FEC机制
丢包重发机制似乎已经可以解决很多问题了,其实在很多场景上,FEC向前恢复机制的处理方式更为重要和普遍。- 一方面丢包重发机制需要重新等待丢失数据包的重发,很多实现上是通过新开另一个通道,甚至用TCP通道来确保丢包数据重发成功。
- 另一方面,丢包重传阶段,接收端要进行短暂等待。在很多使用场景上,是可以接受少部分丢包却不能接受时间效率上的等待,比如音视频直播等。
得益于UDP在数据发送上最大限度对网络带宽进行行了榨取,虽然存在丢包问题,但与TCP顺序串行发送及对于某些场景下效率低下的调节调整机制相比,UDP简单而粗暴但效率却非常的高。
FEC机制通过冗余发送的方式,通对分片进行分组,生成分组数据的冗余包,与数据包一起发送到接收端。接收端在收到数据包后,对分片进行分组恢复,对每个分片中存在丢失的数据包进行反向生成丢失的分片。但理论上单靠FEC不能实现100%数据的恢复。而FEC恢复算法多种多样,有最简单的XOR,还有RS矩阵算法等。
这里提到的是向前纠错方案中最为简单易懂的XOR异或算法。
XOR-FEC分片分组
以XOR异或算法为例,在TPLine中的FEC数据分片的实现流程是这样的:
- 对数据包长度进行分析,计算出最优分组长度。
- 在分组长度上,有对数值范围进行限制的阀值。一方面实现分组长度上不要过大,提高数据的恢复的几率。另一方面要对单个数据包分片分组尽可能均匀,避免最后一个分组元素过短。
- 对单个数据包进行分片并分组,生成FEC分片数据头部。
- 与很多现行的实现方式不同的是,为是方案的实施更加简单,对于TPLine分组中,没有强制要求所有分组中元素的个数都相同。这个集中表现在最后一个分组的数量上。有可能前面分组元素个数都为6, 但最后一个分组的元素个数为4。
- 在进行接收端数据分组重现时,其实可以通过包头中,分片总数与分组长度进行所有分组个数的还原。
- 分组完成后,通过对分组中的每个分片进行“异或”操作,最功生成
冗余分片
。
冗余分片
的长度为MTU
的值- 对包进行分片的过程中,最后一个分片存在大小少于
MTU
值的情况,在生成冗余分片
的时候要进行补码操作,但数据本身不做补码发送。所以最后一个分组可能在最后一个分片长度少于其它分片长度的情况下,也正常进行“异或”操作,生成长度为MTU
值的冗余分片
。
/**
* @param fragmentArray 进行分片后的 单个包的分片集合
* @param length MTU值
**/
+ (NSMutableArray*__nonnull)toSplitFECPackage:(NSMutableArray*__nonnull)fragmentArray length:(int)length{
NSMutableArray *newPackages = [[NSMutableArray alloc] init];
if (fragmentArray != nil && fragmentArray.count > 0){
int count = fragmentArray.count;
//“计算组长”
int gLength = [self countGroupLength:count];
//fec附加包的总个数
int fecCount = count / gLength + (count % gLength > 0? 1 : 0);
int fragmentIndex = 0;
//实际组长, 组长不代表所有组都是这个数量,特别是最后一组有可能少于,可以计算得出
int groupLength = gLength + 1;
Byte *fecByte = (Byte*)malloc(length);
memset(fecByte, 0, sizeof(Byte) * length);
for (int i