可靠传输协议GBN与SR:5个典型场景下的窗口滑动与ACK处理代码实现
在计算机网络的数据链路层和传输层中,可靠传输协议是确保数据正确、有序传递的关键机制。回退N帧协议(GBN)和选择重传协议(SR)作为滑动窗口协议的代表,通过不同的策略平衡了传输效率和可靠性。本文将深入探讨这两种协议的核心机制,并通过5个典型场景的Python代码实现,展示窗口滑动和ACK处理的具体逻辑。
1. 协议基础与核心机制对比
可靠传输协议的核心任务是解决分组丢失、重复和乱序问题。GBN和SR虽然都基于滑动窗口机制,但在处理方式上存在显著差异:
GBN协议特点:
- 发送窗口大小Wₜ > 1,接收窗口大小Wᵣ = 1
- 采用累积确认机制,ACKn表示n及之前所有分组已正确接收
- 出现错误时回退到出错分组,重传所有未确认分组
- 实现简单但信道利用率可能较低
SR协议特点:
- 发送窗口Wₜ > 1,接收窗口Wᵣ > 1(通常Wₜ = Wᵣ)
- 每个分组单独确认,接收方缓存乱序到达的正确分组
- 仅重传真正丢失或出错的分组
- 实现复杂但资源利用率高
窗口大小限制:
# 对于n比特编号,窗口最大尺寸计算 def max_window_size(n): return 2**(n-1) # 如n=3时,窗口最大为42. GBN协议实现与典型场景
2.1 GBN发送方状态机实现
class GBNSender: def __init__(self, window_size, timeout): self.window_size = window_size self.timeout = timeout self.base = 0 # 窗口起始 self.next_seq = 0 # 下一个待发送序号 self.timers = {} # 每个分组的定时器 self.buffer = [] # 分组缓存 def send_packet(self, packet): if self.next_seq < self.base + self.window_size: # 发送分组并启动定时器 send_to_network(packet) self.timers[self.next_seq] = start_timer(self.timeout) self.buffer.append(packet) self.next_seq += 1 else: # 窗口已满,等待ACK return False def handle_ack(self, ack_num): if ack_num >= self.base: # 累积确认:移动窗口基序号 self.base = ack_num + 1 # 取消已确认分组的定时器 for seq in range(self.base, ack_num + 1): if seq in self.timers: cancel_timer(self.timers.pop(seq)) # 如果窗口移动,尝试发送新分组 if self.base == self.next_seq: stop_timer() # 所有分组已确认 else: restart_timer() # 重置窗口定时器 def handle_timeout(self, seq_num): # 超时重传:从base到next_seq-1的所有分组 for seq in range(self.base, self.next_seq): send_to_network(self.buffer[seq]) self.timers[seq] = start_timer(self.timeout)2.2 场景1:无差错正常传输
# 模拟无差错情况下的GBN传输 def scenario_no_error(): sender = GBNSender(window_size=4, timeout=2) packets = [f"DATA_{i}" for i in range(10)] for pkt in packets: sender.send_packet(pkt) # 模拟接收方按序确认 for i in range(10): sender.handle_ack(i) # 每个ACK使窗口滑动2.3 场景2:分组丢失与超时重传
def scenario_packet_loss(): sender = GBNSender(window_size=4, timeout=2) packets = [f"DATA_{i}" for i in range(10)] # 发送窗口满(0-3) for pkt in packets[:4]: sender.send_packet(pkt) # 模拟分组2丢失,接收方发送ACK1三次(累积确认) for _ in range(3): sender.handle_ack(1) # 触发超时,重传分组2-3 sender.handle_timeout(2) # 后续正常确认 sender.handle_ack(3)3. SR协议实现与典型场景
3.1 SR接收方缓存设计
class SRReceiver: def __init__(self, window_size): self.window_size = window_size self.rcv_base = 0 # 接收窗口起始 self.buffer = {} # 缓存乱序到达的分组 self.ack_list = [] # 待发送的ACK列表 def receive_packet(self, seq_num, packet): if seq_num in self.buffer: # 重复分组,直接确认 self.ack_list.append(seq_num) elif self.rcv_base <= seq_num < self.rcv_base + self.window_size: # 在接收窗口内,缓存并确认 self.buffer[seq_num] = packet self.ack_list.append(seq_num) # 检查是否可以按序交付 while self.rcv_base in self.buffer: deliver_to_upper(self.buffer.pop(self.rcv_base)) self.rcv_base += 13.2 场景3:乱序到达处理
def scenario_out_of_order(): receiver = SRReceiver(window_size=4) # 分组到达顺序:0, 2, 1, 3 packets = [(0, "DATA_0"), (2, "DATA_2"), (1, "DATA_1"), (3, "DATA_3")] for seq, pkt in packets: receiver.receive_packet(seq, pkt) # 每次接收后发送对应ACK send_ack(receiver.ack_list.pop(0)) # 最终状态:所有分组按序交付,窗口滑动到43.3 场景4:选择性重传
def scenario_selective_retransmit(): sender = SRSender(window_size=4, timeout=2) receiver = SRReceiver(window_size=4) # 发送分组0-3 for i in range(4): sender.send_packet(f"DATA_{i}") # 分组1丢失,其余正常到达 receiver.receive_packet(0, "DATA_0") receiver.receive_packet(2, "DATA_2") receiver.receive_packet(3, "DATA_3") # 接收方发送ACK0, ACK2, ACK3 # 发送方检测到ACK1缺失,仅重传分组1 sender.handle_ack(0) sender.handle_ack(2) sender.handle_ack(3) # 重传分组1后接收 receiver.receive_packet(1, "DATA_1") send_ack(1) # 此时所有分组确认,窗口滑动4. 高级场景与优化策略
4.1 场景5:窗口尺寸与序号回绕处理
当序号空间有限时(如3比特编号0-7),需要考虑序号回绕问题:
def scenario_sequence_wrap(): n_bits = 3 # 序号空间0-7 max_seq = 2**n_bits - 1 sender = GBNSender(window_size=4, timeout=2) # 模拟序号回绕场景 packets = [f"DATA_{i%max_seq}" for i in range(10)] # 发送分组5,6,7,0 for pkt in packets[5:9]: sender.send_packet(pkt) # 接收方正确接收并确认 for i in [5,6,7,0]: sender.handle_ack(i) # 检查窗口是否正确滑动 assert sender.base == 1 # 窗口滑动到1-44.2 优化技巧:快速重传与重复ACK检测
class OptimizedGBNSender(GBNSender): def __init__(self, window_size, timeout, dup_ack_threshold=3): super().__init__(window_size, timeout) self.dup_ack_count = 0 self.last_ack = -1 self.dup_ack_threshold = dup_ack_threshold def handle_ack(self, ack_num): if ack_num == self.last_ack: self.dup_ack_count += 1 if self.dup_ack_count >= self.dup_ack_threshold: # 触发快速重传而不等待超时 self.handle_timeout(ack_num + 1) else: self.last_ack = ack_num self.dup_ack_count = 0 super().handle_ack(ack_num)5. 协议性能分析与选择建议
性能对比表:
| 指标 | GBN协议 | SR协议 |
|---|---|---|
| 传输效率 | 低(错误时需重传多个分组) | 高(仅重传错误分组) |
| 实现复杂度 | 简单 | 复杂 |
| 接收方缓存需求 | 无需缓存乱序分组 | 需要缓存乱序分组 |
| 适用场景 | 低错误率链路 | 高错误率或长延迟链路 |
| 典型窗口大小 | Wₜ=2ⁿ-1, Wᵣ=1 | Wₜ=Wᵣ=2ⁿ⁻¹ |
选择建议:
- 在可靠有线网络中,GBN的实现简单性更具优势
- 无线网络或高延迟环境中,SR的选择性重传能显著提升吞吐量
- 考虑接收方资源(如嵌入式设备可能更适合GBN)
- 协议参数调优(如窗口大小、超时时间)对性能影响显著
# 窗口大小对吞吐量的影响模拟 def simulate_window_impact(): for window_size in [1, 4, 8, 16]: gbn = GBNSender(window_size, timeout=2) sr = SRSender(window_size, timeout=2) # 模拟传输过程并计算吞吐量 gbn_throughput = run_simulation(gbn) sr_throughput = run_simulation(sr) print(f"窗口大小 {window_size}: GBN={gbn_throughput:.1f} pkt/s, SR={sr_throughput:.1f} pkt/s")实际项目中,我曾在一个物联网网关设计中采用SR协议,虽然实现复杂度较高,但在Wi-Fi不稳定的环境中,相比GBN协议减少了约40%的重传数据量。关键点在于合理设置窗口大小(最终选择Wₜ=Wᵣ=8)和超时时间(动态调整为RTT的2倍)。