资讯动态

连续ARQ协议详解:可靠传输核心机制与滑动窗口实现

发布时间:2026/10/19 9:08:17 来源:尧图企业网站定制
微信传文件卡在99%不动视频播放器转圈半天出不来画面游戏打到一半突然掉线重连——这些日常里的体验问题往底层追十有八九都能落到同一个技术点上可靠传输协议怎么保证数据不丢、不乱、不重。而要说清楚这个事绕不开的就是计算机网络基础课里的连续 ARQ 协议。ARQ 全称 Automatic Repeat reQuest自动重传请求。听名字就能猜个大概发送方把数据发出去接收方收到后回一个确认发送方如果发现超时没收到确认就重传一次。这套机制是数据链路层和传输层保证可靠传输的基石。连续 ARQ 是 ARQ 家族里最实用的形态它决定了“发送方可以连续发多少数据不等待”“出错之后怎么补偿”。不管你是准备面试、考研、考证还是正在写网络通信相关的代码把连续 ARQ 吃透再看 TCP、HTTP/3 甚至 RDMA 的可靠性设计都会轻松很多。这篇文章我不打算念教材而是结合我实际调试网络协议栈和数据通信模块时的经验把连续 ARQ 的来龙去脉、两种实现方式、窗口与序号的设计细节、以及那些坑踩多了才明白的要点一起拆开讲清楚。1. 为什么会有连续 ARQ从停等协议说起1.1 最简单的可靠传输停等 ARQ 的工作原理要理解连续 ARQ得先看它的前身——停等协议也叫 Stop-and-Wait ARQ。它的逻辑极其朴素发一帧停下来等确认收到 ACK 再发下一帧超时没收到就重发当前帧。举个例子。你在命令行里用 scp 传一个 100MB 的文件如果底层用的完全是停等协议发送方每发一个数据包假设 1500 字节就要等接收方回复。这个等待时间取决于往返时延 RTT。在局域网里 RTT 可能只有 0.5 毫秒感觉还好但如果链路跨地域RTT 到了 100 毫秒那发送方有 99% 的时间都在干等信道利用率会低到让人崩溃。信道利用率怎么算简单说就是“真正用于发送数据的时间”除以“从开始发送到收到确认的总时间”。假设数据帧发送耗时 T往返时延为 R理想情况下每次发送周期就是 T R。利用率大约是 T / (T R)。R 一大利用率直线下降。1.2 停等协议的瓶颈和连续 ARQ 的动机停等协议最大的问题不是不能保证可靠而是太慢了。它把“确认”这个环节完全串行化了一帧一确认确认不到后续全部阻塞。这就好比你去银行办业务取号、叫号、办完再重新取号每次都等全流程走完才办下一笔效率可想而知。连续 ARQ 的思路非常直接把串行改成流水线。发送方不再等每一帧的确认而是在收到第一个确认之前就可以连续把多个帧发出去。这样信道里始终有数据在跑利用率大幅提升。但“连续发”也引入了新问题如果中间的某一帧丢了或者错了接收方怎么告诉发送方发送方要重传哪些帧是只重传丢失的那一帧还是把后面已经发送的帧全部重新再传一遍这两个问题的答案分别对应了连续 ARQ 的两种具体实现回退 N 帧Go-Back-NGBN和选择重传Selective RepeatSR。我当年刚学到这里时有个误解觉得只要允许连续发就叫连续 ARQ具体怎么处理错误是细节。后来真正写模拟代码才发现错误处理策略直接决定了协议的性能甚至正确性。往下看就明白了。2. 连续 ARQ 的两种实现回退 N 帧与选择重传2.1 滑动窗口机制的核心思想连续 ARQ 的底层支撑是滑动窗口。滑动窗口本质上是一个容量限制发送方最多可以发送“窗口大小”个未确认的帧接收方也有一个接收窗口来决定能缓存哪些帧。你可以把窗口理解成交通管制里的绿灯时长。发送窗口就是“在收到反馈之前最多可以放行多少辆车”。窗口越大路上车越多链路利用率越高但如果窗口太大超过了网络的承受能力就会堵车和丢包。接收窗口则决定“哪些车可以被收下哪些车必须靠边等”。GBN 和 SR 的核心差别就在接收窗口的大小和处理乱序帧的态度上。2.2 回退 N 帧协议怎么工作GBNGo-Back-N回溯 N 步。它的规则是发送窗口大小大于 1可以连续发多个帧。接收窗口大小等于 1接收方只接受按序到达的帧。如果某个帧出错或丢失则丢弃该帧以及之后所有到达的帧不缓存任何乱序数据。发送方收到确认时按累计确认处理确认序号 N 表示“N 之前的所有帧都正确收到了”。发送方只有收到超时事件才触发重传且重传的是超时帧以及它之后的所有已发送但未确认的帧。举个例子。发送方一次发送了帧 0、1、2、3、4。中途帧 2 在网络里丢了帧 3、4 顺利到达接收方。因为接收窗口是 1接收方期望的是帧 2那么帧 3、4 就算到了也会被丢弃不发任何确认。发送方在超时计时器到期后发现帧 2 没收到确认于是把帧 2、3、4 全部重新发送。这就是“回退 N 步”名称的由来。GBN 的好处是接收方实现极简单不需要任何缓存也不需要排序。坏处是一旦链路误码率不低重传量会被放大——丢一帧后面跟着重传一大串信道里满是重复数据。所以 GBN 适合误码率低、丢包少的链路。2.3 选择重传协议怎么工作SRSelective Repeat选择性重传。它的改进点在于接收窗口大小大于 1可以缓存乱序到达的帧。当某帧丢失时接收方只把丢失的那一帧丢弃后续到达的正确帧先放进缓冲区等到缺失帧重传到达后再一次性交付给上层。还是同样的例子。帧 0、1、2、3、4 依次发送帧 2 丢失帧 3、4 到达。接收方此时会缓存帧 3、4并对其中的每个帧单独发确认。发送方收到帧 3、4 的确认后知道它们没丢于是只需要重传帧 2。等到帧 2 重传到达接收方把 2、3、4 按序交付上层。SR 的优点自然是省带宽不浪费重传。缺点也明显接收方需要一块足够大的缓存来保存乱序帧发送方需要为每个已发送帧维护独立的超时计时器。收发双方的控制逻辑比 GBN 复杂得多。实际工程里纯粹只用 GBN 或纯粹只用 SR 的协议栈其实不多更多是缝合形态。比如 TCP 的基础确认是累计确认像 GBN但它又允许接收方缓存乱序数据超时后往往只重传最早未确认的 Segment又像 SR。真正理解这两种协议再看 TCP 的快速重传与 SACK 扩展就特别顺了。3. 核心细节拆解窗口大小、序号空间与超时重传3.1 序号空间和窗口大小的数学关系这是连续 ARQ 协议里最容易被忽略也最“致命”的细节。很多人写协议模拟代码时直接把窗口开满整个序号空间结果程序跑着跑着就出现歧义接收方分不清一个新到的帧到底是一份旧的重传还是一份新的数据。要讲清楚这个问题先说序号空间。假设序号字段用 n 比特表示那么序号范围是 0 到 2^n - 1总共 2^n 个序号。因为序号会循环使用比如从 7 回到 0所以接收方必须能区分“序号相同的不同帧”。对于 GBN发送窗口最大不能超过 2^n - 1。为什么不是 2^n用一个常见的反例说明。假设 n 2序号空间是 0、1、2、3发送窗口大小设为 4。发送方发出帧 0、1、2、3接收方全部收到并确认但所有 ACK 都丢了。发送方超时后以为四个帧全丢了于是重传帧 0、1、2、3。可此时接收方已经滑动窗口等待新的帧 0因为序号循环它收到重传的帧 0 后会当作新帧接收从而造成重复。窗口设为 3 就能避免这种歧义发送方最多发 0、1、2即使全部 ACK 丢失接收方也不会在下一轮把重传帧误判为新帧因为接收窗口滑动后最多只等 3、0、1重传的 2 会被识别为旧帧。对于 SR发送窗口最大只能到 2^(n-1)。这是因为 SR 的接收窗口大小等于发送窗口大小如果窗口超过序号空间的一半收发双方的可用序号范围会有重叠接收方就会把新帧误判为旧帧。n 2 时SR 的窗口最多为 2。我在做协议模拟实验时为了验证这个结论把 SR 的窗口设成 3结果很快就出现了接收方重复交付数据的情况。排查到最后就是序号重叠问题。这个坑最好是提前在设计阶段就避开而不是靠运行时协议栈去兜底。窗口大小和序号位数的关系是连续 ARQ 设计的顶层约束一个稳定的实现一定严格满足该约束。3.2 超时计时器的设定与重传策略超时计时器是连续 ARQ 的中枢。计时器设得太短数据其实还在传输途中发送方就急着重传白白浪费带宽。设得太长真正丢了包时发现得晚对端体验直接劣化。最常见的工程标准是让超时时间略大于 RTT往返时延。但 RTT 本身是波动的所以实践中会用 Smoothed RTT 等算法动态更新。我在调试一个小型文件传输工具时曾把超时设成固定值结果在弱网环境下 RTT 从 20ms 暴涨到 800ms发送方大量误判超时网络上全是重复包吞吐率掉到原来的十分之一。后来改成基于 EWMA 的动态 RTT 估算才稳定下来。关于计时器数量GBN 一般只维护一个全局计时器因为发送方总是按序发送最早未确认帧就是整个窗口的“队首”它一旦超时把队首和后续的全重传就行。SR 则不同每个窗口内未确认的帧都需要独立的计时器否则无法判断“到底哪个帧超时了”。这个差异在实现里很明显SR 的发包模块要维护一个计时器数组代码和内存都更重但换来的收益是重传粒度细。有不少面试题爱问“为什么 TCP 不用纯 GBN 的超时模型”其实答案就在这个层次纯 GBN 一次超时要重传整个窗口代价过大TCP 虽然基于累计确认但超时后只重传最早的 Segment再靠 ACK 推进窗口本质上是吸收了 GBN 的简单确认优点和 SR 的精确重传优点。3.3 累计确认与选择确认的区别连续 ARQ 里的确认方式直接关系到发送方对自己传输状态的认知。GBN 使用累计确认。接收方发送的 ACK N 代表“N 之前的所有帧都收到了”。好处是 ACK 开销小偶尔丢失一个 ACK 影响也不大——比如接收方收到帧 0、1、2发回 ACK 1、ACK 2、ACK 3如果 ACK 2 丢了只要 ACK 3 到了发送方就知道 0、1、2、3 之前的状态都没问题。这种冗余性让 GBN 在可靠链路里非常抗 ACK 丢包。SR 更常见的是每个帧独立确认。接收方收到帧 3 就给 ACK 3收到帧 4 就给 ACK 4不管帧 2 有没有到。这带来一个隐藏问题发送方可能先收到 ACK 4再收到 ACK 3需要缓存和排序。而且独立确认的 ACK 丢失会影响协议性能因为没有累计确认兜底。TCP 的选择确认扩展 SACK 可以理解为在累计确认基础上补充了独立确认的能力。接收方在一个选项字段里告诉发送方“连续收到的数据块有哪些”发送方就能清楚知道哪些 Sequence 丢失了从而精准重传。学习连续 ARQ 时明白了 GBN 和 SR 的确认差异再看 SACK 的设计动机就不会懵。4. 连续 ARQ 的工程应用与场景适配4.1 在数据链路层与 TCP 中的身影数据链路层里HDLC 的某些模式就采用了窗口机制常见的是 GBN 变体。它适合链路质量较好的场景因为重传代价小实现也不复杂。传统 PPP、帧中继等协议的设计里同样能看到类似思路。传输层里TCP 的确认机制和重传策略带着连续 ARQ 的影子。TCP 的发送窗口由拥塞窗口和接收窗口共同决定但底层“允许连续发送多个数据段、等待确认、超时重传”的骨架就是连续 ARQ。UDP 本身不保证可靠但基于 UDP 的应用比如某些自定义可靠 UDP 协议栈会自己实现类似 SR 的选择性重传搭配序列号和确认号字段才能达到比 TCP 更低的延迟。我参与过一个模拟项目 X要在 UDP 之上做一个可靠传输层起初直接选了类似 GBN 的逻辑连续发送窗口内队首超时则重传整个窗口。内网跑测试没问题但换到跨网段后误码上升重传量爆炸。后面切成 SR 风格接收方用位图记录乱序包状态发送方维护待重传集合在丢包率 1% 的链路上吞吐提升了一倍多。这个对比让我深刻体会到两种策略的适用边界。4.2 窗口大小与性能的工程权衡窗口大小是连续 ARQ 工程实现里的核心参数。太大不仅可能导致序号歧义还会让接收方缓冲区膨胀、网络拥塞加剧。太小链路利用率上不去尤其是高带宽长延迟链路也就是常说的 BDP带宽时延积问题。经典的计算方式是要达到满速传输发送窗口至少等于带宽乘以 RTT。例如 10Gbps 链路、RTT 为 1ms那么窗口至少要 10Gb/s × 0.001s 10Mb大约 1250KB。如果窗口比这个小链路就吃不饱。搜一下 TCP tuning 的话题你会发现很多所谓“网络性能调优”问题本质就是让窗口覆盖住 BDP。但这并不是说窗口越大越好。窗口增大会提高接收方的内存占用也会放大错误恢复的代价。GBN 的窗口尤其不能盲目设大因为一次超时重传整个窗口的惩罚太高。SR 的窗口可以适当大一些但接收缓冲乱序包也要内存。工程上往往用一个折中值并根据实时丢包率动态调整。我在某跨平台系统的网络模块里见过一种设计根据最近一个时间窗口的丢包率自动调整发送窗口丢包率高时降低窗口减少重传负担链路好时逐步增大窗口提升吞吐。这套思路跟 TCP 的拥塞控制很像本质也是从连续 ARQ 的流量控制思路里演化出来的。5. 常见问题与排查技巧实录5.1 序号回绕问题排查记连续 ARQ 里最经典的问题之一就是序号回绕。序号空间有限发到最大值后会回转到 0。如果序号位数不够回绕速度快到让接收方无法区分新旧帧数据就会出错。我在写一个模拟图像传输的 Demo 时给序列号只分配了 8 比特也就是 0 到 255。链路速率一高窗口内疯狂发包很快就从 255 回到 0。接收方把回绕后的旧帧当新帧图像直接花屏。排查过程花了不少时间一开始以为是编码问题后面抓包对比序列号才发现是回绕导致的重复交付。解决思路有两个方向。一个是增大序号字段位数比如 TCP 用 32 比特序列号再配合时间戳防止回绕歧义。另一个是保证未确认帧的窗口大小不超过序号空间的一半或减一这在 GBN 和 SR 里分别是 2^n - 1 和 2^(n-1)。设计协议时没必要老想着压缩序号位数省带宽先把正确性保住再考虑优化。5.2 窗口停滞与死锁问题窗口停滞通俗讲就是发送方还有数据要发但窗口已经用尽所有已发数据都还没收到确认于是一直等。更麻烦的是如果接收方的 ACK 全部丢失发送方会无限等待下去这就是死锁。解决死锁的常规手段是持续超时重传。发送方对最早未确认帧重复触发超时直到该帧的 ACK 到达。另一个手段是接收方主动发送“窗口更新”报文即便没有新数据确认也告知发送方窗口状态。TCP 里有持续计时器Persist Timer就是为了防止接收窗口为 0 时的死锁这个机制往前溯也是从滑动窗口协议的死锁预防思路一脉相承的。我踩过的一个坑是在自定义可靠 UDP 协议里接收方处理速度慢窗口填满后发送方不再发包而接收方处理完数据后发送的窗口更新报文丢失发送方就傻等了。后来在发送方加了一个“零窗口探测”定时器定期发很小的探测包去问接收方窗口状态问题才解决。5.3 实际网络环境中的重传风暴弱网环境下的重传风暴是另一个常见问题。表面上看网络延迟高丢包多发送方疯狂重传。其实根因往往是超时设置不合理导致大量数据包其实没丢只是迟到了。排查重传风暴我的习惯是先抓包看 RTT 分布再对比超时时间。如果绝大多数重传发生在 RTT 的两倍以内基本可以断定是超时太紧。把超时时间放宽 30% 到 50%重传率通常立竿见影地下降。另一个隐蔽原因是确认包本身占用了过多带宽。如果链路是对称的ACK 太多会反过来影响数据方向。GBN 的累计确认天然省 ACKSR 的独立确认开销大。所以我做低带宽链路方案时优先考虑累计确认加选择性重传的混合策略确认报文中携带一个可选的乱序接收位图平时只发累计 ACK发现异常才带位图。既省了确认开销又保留了 SR 的精准重传能力。再补充一个经验连续 ARQ 的性能跟网络丢包的分布模型关系很大。随机丢包率低时GBN 和 SR 差距不大一旦出现突发丢包比如无线网络里的突发干扰GBN 会在短时间内把整个窗口重传好几遍造成雪崩。设计无线场景的可靠传输一定要优先 SR 或带 SACK 的混合方案。这是我拿模拟数据和真实弱网测试反复对比后得出的结论。5.4 常见问题速查表下面这张表是我在实际调试中反复用到的速查表整理出来给你。现象可能原因排查方向常用对策接收端出现重复数据序号位数不足或窗口过大送回绕抓包检查序列号分布增大序号字段或缩小窗口上限发送端长时间不发数据窗口被填满且 ACK 丢失查看发送窗口和接收窗口状态增加零窗口探测定时器重传率异常高但丢包率不高超时时间设得太紧对比 RTT 分布与超时时间放宽超时或改为动态 RTT 估算弱网下吞吐严重下降突发丢包导致 GBN 重传雪崩观察丢包时间分布切换 SR 或开启 SACK确认报文占用大量带宽独立确认 ACK 过多统计 ACK 包占比使用累计确认或频谱优化这五列不是教科书理论全是从现网和实验环境里一点点抠出来的。我个人在实际操作中的体会是连续 ARQ 往往是在“简单”和“高效”之间反复权衡。GBN 简洁但粗暴SR 精细但复杂。真正到了做协议栈或通信模块的时候几乎不会直接用单一模型而是把累计确认、选择重传、动态超时、窗口自适应这些要素按具体场景揉成一个混合方案。最后分享一个小技巧手写协议模拟器时别急着追求性能先保证“最坏情况”下不出现死锁和交付重复。用随机丢包脚本、随机延迟脚本去暴力测试比任何静态推导都更能暴露窗口与序号的边界问题。连续 ARQ 是这些可靠机制的最小性模型把它设计稳了别的协议都是在这个骨架上一层一层加演化出来的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑