RTP 包的序列号范围:[0, 2 ^ 16-1],当序列号达到最大值时,会出现所谓的回绕。例如,序列号到了 2 ^ 16-1,下个包序列号就是0。所以在判定一个包是新的数据包,还是重传或者乱序包时,不能直接根据数学意义上的大小值进行序列号比较。
WebRTC 使用如下算法来解决数字回绕问题:
inline bool IsNewerSequenceNumber(uint16_t sequence_number,
uint16_t prev_sequence_number) {
// Distinguish between elements that are exactly 0x8000 apart.
// If s1>s2 and |s1-s2| = 0x8000: IsNewer(s1,s2)=true, IsNewer(s2,s1)=false
// rather than having IsNewer(s1,s2) = IsNewer(s2,s1) = false.
if (static_cast<uint16_t>(sequence_number - prev_sequence_number) == 0x8000) {
return sequence_number > prev_sequence_number;
}
return sequence_number != prev_sequence_number &&
static_cast<uint16_t>(sequence_number - prev_sequence_number) < 0x8000;
}
其原理是:
-
定义两个 uint16 变量:sequence_number(当前收到的包的序列号),prev_sequence_number(上一个包的序列号);
-
定义一个常量:Breakpoint,值为 uint16 类型取值范围的一半,即为 0x8000 = 8*16^3 = 32768;
-
若 |sequence_number - prev_sequence_number| = Breakpoint,则以 sequence_number 与 prev_sequence_number 的大小作为判断依据;
-
若 sequence_number != prev_sequence_number 且 |sequence_number - prev_sequence_number| < Breakpoint,则当前包为新包;
-
若为其他情况,则当前包为重传或者乱序包。