滑动窗口机制及其应用
在无线通信开发中,如何高效跟踪数据包的接收状态是一个常见难题。由于无线信号易受干扰,数据包可能丢失、重复或乱序到达,这就需要一套机制来记录哪些数据包已收到、哪些需要重传。
什么是滑动窗口机制?
滑动窗口(Sliding Window)是网络通信和无线协议中常用的流量控制与状态跟踪技术。它通过定义一个固定大小的 "窗口",只关注窗口范围内的数据包状态,随着新数据的接收,窗口不断向前滑动,既保证了状态跟踪的效率,又能适应序列号循环复用的场景。
在本文解析的代码中,滑动窗口机制主要解决三个核心问题:
- 如何标记哪些序列号的数据包已接收
- 如何处理数据包丢失、重复和乱序到达的情况
- 如何应对序列号回绕(如 8 位 SN 从 255 回到 0)的场景
代码实现
这里我们同时使用了滑动窗口+位图的形式,位图的方式存储位置,在理论上可以实现极为高效的资源利用率
class SlidingWindowReceiver:def __init__(self, window_size=32):# 窗口大小self.window_size = window_size# 接收位图,用整数的二进制位表示接收状态self.rx_bitmap = 0# 最后处理的序列号,初始化为特殊值表示未初始化self.update_index = -1 # 相当于NML_HRF_UPDATE_INDEX_INIT# 8位序列号的最大值self.UINT8_MAX = 255def update_rx_status(self, sn):"""更新接收状态"""# 处理重复包if self.update_index == sn:print(f"丢弃重复包,SN: {sn}")returnlast_update_index = self.update_indexdelta = 0# 计算序列号差值deltaif last_update_index == -1: # 初始状态强制delta为1delta = 1else:if sn > last_update_index:delta = sn - last_update_indexelse:# 处理序列号回绕delta = (self.UINT8_MAX + 1 + sn - last_update_index)# 转换为有符号8位整数if delta > 127:delta -= 256# 根据delta更新窗口if delta == 1:# 连续接收,更新窗口起点print(f"连续接收,SN: {sn}")self.update_index = snelif delta > 0:# 超前接收,标记中间丢失的包print(f"超前接收,SN: {sn},丢失 {delta - 1} 个包")for i in range(delta - 1):# 计算丢失的序列号lost_sn = last_update_index + 1 + i# 计算对应的位位置bit_pos = lost_sn % self.window_size# 清除该位(标记为未接收)self.rx_bitmap &= ~(1 << bit_pos)self.update_index = snelse:# 滞后接收(收到旧包)print(f"收到旧包,SN: {sn}")# 标记当前SN为已接收bit_pos = sn % self.window_sizeself.rx_bitmap |= (1 << bit_pos)def print_status(self):"""打印当前接收状态"""print(f"\\n当前窗口起点: {self.update_index}")print(f"接收位图(二进制): {bin(self.rx_bitmap)[2:].zfill(self.window_size)}")print(f"已接收的SN(窗口内): {[i for i in range(self.window_size) if (self.rx_bitmap & (1 << i))]}")# 测试滑动窗口机制
if __name__ == "__main__":# 创建一个窗口大小为8的接收器receiver = SlidingWindowReceiver(window_size=8)# 模拟接收一系列数据包test_sequence = [1, 3, 2, 5, 8, 9, 7, 10, 15, 14, 16, 3]for sn in test_sequence:print(f"\\n处理SN: {sn}")receiver.update_rx_status(sn)receiver.print_status()