WebRTC 序列号回绕问题

RTP协议中有序列号字段 sequence number ,这个字段主要用于丢包、乱序的处理。RTP 包的头部序列号字段长度为 16 bits,取值范围为 [0, 65535]。

实际项目中需要解决的问题:对于两个 RTP 包,如何比较哪一个包才是最新的包?

比如,序列号为 0 的包一定比序列号为 65535 的包小,是旧的包吗?再如,序列号为 65535 的包一定比序列号为 255 的包大,是最新的包吗?这就涉及到回绕的问题。

序列号回绕

对于无符号类型的数据来说,当到达临界值时继续递增,就会出现回绕。序列号的回绕方式有两种,分别是向前回绕和向后回绕。

前向回绕(forward wrap)

所谓前向回绕,就是顺时针回绕,只有在序列号递增的情况下才会发生。有如下特点:

  • 包号呈向前递增趋势
  • 当前的包号却比上一个包号小
  • 从上一个包号到当前的包号,需要跨越0这个数
  • 包号之间的距离小于包号类型能表示的数字个数的一半
  • 逻辑上认定当前包号才是更新更大的包

后向回绕(backward wrap)

向后回绕是逆时针的回绕,有如下特点:

  • 包号呈向后递减趋势
  • 当前的包号很大,而前一个包号很小
  • 从上一个包号向后跨越包号 0 到当前的包号
  • 包号之间的距离大于包号类型能表示的数字个数的一半
  • 逻辑上认定当前包号是更小的包号,即旧的包

如何判断两个u16数大小

实现代码

#define UINT16_MAX        65535
#define U16_K_BREAKPOINT ((UINT16_MAX >> 1) + 1)

bool isNewerSeqNum(UINT16 value, UINT16 prevValue) {
	// 32768 ... 是U16最大值的一半
	uint16_t breakPoint = U16_K_BREAKPOINT;
	// 如果两值差正好对半,直接与前值判断大小即可
	if (value - prevValue == breakPoint) {
		return value > prevValue;
	}
	// 如果重叠,不会算是新包
	if (value == prevValue) {
		return false;
	}
	bool ret = (UINT16)(value - prevValue) < breakPoint;
	return ret;
}

测试场景

测试需覆盖:包号相等、包号无回绕、包号向前回绕、包号向后回绕、包号距离为临界值(32768)这五种场景。

1)包号相等

//
// |____________________|____________________|
//                 ^    0                    32768
//               65530
//
void wrap_test() {
	uint16_t seqNum = 65530;
	uint16_t prevSeqNum = 65530;
	bool ret = isNewerSeqNum(seqNum, prevSeqNum);
	printf("seqNum:%d prevSeqNum:%d return:%d\n", seqNum, prevSeqNum, ret);
}

2)包号无回绕

//
// |____________________|____________________|
//                      0  ^      ^          32768
//                        3278   6553
//                         |---->  |---->
//
void wrap_test() {
	uint16_t seqNum = 6553;
	uint16_t prevSeqNum = 3278;
	bool ret = isNewerSeqNum(seqNum, prevSeqNum);
	printf("seqNum:%d prevSeqNum:%d return:%d\n", seqNum, prevSeqNum, ret);
}

3)包号向前回绕

//
// |____________________|____________________|
//                 ^    0                ^   32768
//               65530                 32761
//            ---->|                     |---->
//
void wrap_test() {
	uint16_t seqNum = 32761;
	uint16_t prevSeqNum = 65530;
	bool ret = isNewerSeqNum(seqNum, prevSeqNum);
	printf("seqNum:%d prevSeqNum:%d return:%d\n", seqNum, prevSeqNum, ret);
}

4)包号向后回绕

//
// |____________________|____________________|
//                 ^    0                ^   32768
//               65530                 32761
//                 |<----           <----|
//
void wrap_test() {
	uint16_t seqNum = 65530;
	uint16_t prevSeqNum = 32761;
	bool ret = isNewerSeqNum(seqNum, prevSeqNum);
	printf("seqNum:%d prevSeqNum:%d return:%d\n", seqNum, prevSeqNum, ret);
}

5)包号距离为临界值

//
// |____________________|____________________|
//                      0                    32768
//                      ^                    ^
//                 ---->|                    |---->
//
void wrap_test() {
	uint16_t seqNum = 0;
	uint16_t prevSeqNum = 32768;
	bool ret = isNewerSeqNum(seqNum, prevSeqNum);
	printf("seqNum:%d prevSeqNum:%d return:%d\n", seqNum, prevSeqNum, ret);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

毕加索解锁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值