TCP的拥塞控制算法中的PRR(Proportional Rate Reduction)机制详解
字数 3767 2025-12-10 09:53:00

TCP的拥塞控制算法中的PRR(Proportional Rate Reduction)机制详解

一、 问题/知识点描述
PRR是一种在TCP快速恢复阶段,用于精确控制数据发送速率,以更快、更平稳地从丢包中恢复的算法。它于2011年被提出,并被集成到Linux内核的TCP实现中,旨在替代传统快速恢复中较为粗糙的速率控制方法。

核心问题:传统的快速恢复算法(如Reno、NewReno)在收到部分ACK(例如,触发快速重传的3个重复ACK)后,会将拥塞窗口(cwnd)减半,并进入恢复阶段。在这个阶段,发送端每收到一个ACK,就可以发送新数据。这可能导致发送速率突然下降后又突然飙升(因为cwnd减半但实际可发送量可能瞬时较多),造成发送“突发”(burst),加剧网络拥塞或导致不必要的丢包。PRR的目标是让发送速率在恢复阶段平滑地、按比例地降低到新的、减半后的cwnd值,并保持在这个水平,直到恢复结束。

二、 循序渐进的讲解

步骤1:场景设定与基础回顾

  1. 假设TCP连接正在正常传输,其cwnd(拥塞窗口)为20个报文段(MSS),ssthresh(慢启动阈值)为15。
  2. 发生了一个数据包丢失。接收端因为后续数据包的到达,会发送重复的ACK(Dup-ACK)。
  3. 发送端收到第3个Dup-ACK,触发快速重传。此时,按照经典算法(如NewReno):
    • ssthresh更新为:ssthresh = max(在外数据包数 (FlightSize) / 2, 2)。假设FlightSize为20,则ssthresh = 10
    • cwnd更新为:cwnd = ssthresh + 3(因为收到了3个Dup-ACK,相当于“信用”)。所以cwnd = 13
    • 进入快速恢复阶段。

步骤2:传统快速恢复的发送行为(作为对比)
在快速恢复阶段,传统算法(如NewReno)的发送规则是:每收到一个ACK(无论是重复ACK还是新数据的ACK),就可以发送一个新数据包

  • 问题:当cwnd从20骤降到13后,但FlightSize(在外数据包数)可能因为重传和新发送而迅速变化。这个规则可能导致发送端在恢复初期,因为收到多个Dup-ACK,连续发送多个数据包,形成一个小的“突发流”。虽然cwnd限制了总量,但发送节奏不均匀。

步骤3:PRR算法的核心思想
PRR的核心是:在快速恢复阶段,发送的数据总量应被精确控制,使得在恢复过程结束时,在外数据包数(FlightSize)恰好等于新的、减半后的拥塞窗口(ssthresh。并且,这个发送过程应该是按比例均匀分布的。

  • 目标prr_delivered(进入恢复阶段后成功递送的数据量)与prr_out(进入恢复阶段后发送的数据量)保持一个恒定的比例,直到恢复完成。
  • 公式核心:它定义了在恢复期间,每确认一个旧数据包,允许发送多少新数据包

步骤4:PRR算法的两个模式与关键变量
PRR定义了两个模式:PRR=SS(慢启动风格)和PRR=CRB(保守速率削减)。模式的选择基于一个关键变量:recover

  1. 变量初始化(当触发快速重传,进入恢复阶段时):

    • prr_delivered = 0 (进入恢复后,累计确认交付的字节数)
    • prr_out = 0 (进入恢复后,累计发送的字节数)
    • ssthresh = 当前计算的新的慢启动阈值(如步骤1中的10)
    • pipe = 当前估算的在外数据包数(一个动态值,每发送+1,每确认-1)。
    • recover = snd.nxt (下一个要发送的序列号)。这是恢复阶段的“终点”标志。所有序列号小于recover的数据,都属于这次恢复要处理的范围。
  2. 模式选择(在每次决定能否发送数据时):

    • 计算 prr_delivered 自恢复开始以来的增量。
    • 如果当前snd.nxt(下一个要发送的序列号) < recover,说明我们还在重传或发送“旧”的、在恢复范围内的数据。此时使用 PRR=SS模式。
    • 如果当前snd.nxt >= recover,说明所有旧数据都已重传或发送完毕,开始发送全新的数据。此时使用 PRR=CRB模式。通常PRR=CRB允许更保守的发送速率。

步骤5:PRR发送量计算(核心算法)
每当一个数据包被确认(ACK到达),算法计算本次可以发送多少数据:

  1. 更新 prr_delivered:增加刚被确认的字节数。
  2. 计算允许发送量
    • PRR=SS模式(慢启动风格)
      • limit = prr_delivered * ssthresh / RecoverFS - prr_out
      • 其中RecoverFS是进入恢复瞬间的FlightSize(例子中的20)。
      • 这个公式确保发送总量prr_out与交付总量prr_delivered的比例为 ssthresh/RecoverFS(例子中是10/20=0.5)。即每交付2个字节,只允许发送1个新字节,从而平滑地将管道中的数据量从20削减到10。
    • PRR=CRB模式(保守速率削减)
      • limit = (prr_delivered - prr_out) 加上一些微调。
      • 这个模式更严格,基本保证prr_out不会超过prr_delivered太多,确保恢复后FlightSize稳定在ssthresh附近,不会增长。
  3. 发送决策:计算出的limit表示当前允许发送的字节数“信用”。只有当limit > 0且当前pipe(估算在途数据)小于cwnd(注意,在PRR中,cwnd在恢复阶段通常被冻结为ssthresh,不用于直接限制发送,而是作为pipe的上界)时,才能发送新数据包。

步骤6:结合示例说明流程
接步骤1:cwnd_new = ssthresh = 10, RecoverFS = 20

  • 时刻T0(进入恢复)prr_delivered=0, prr_out=0, pipe≈20, snd.nxt指向丢失的包(序列号X)。recover设为当前snd.nxt
  • 时刻T1(收到一个Dup-ACK,确认了序列号X-1的包)
    • prr_delivered增加1个MSS。
    • 模式:snd.nxt (X) < recover -> PRR=SS。
    • limit = 1 * 10 / 20 - 0 = 0.5。由于不能发送半个包,通常实现会累积信用。假设信用足够发1个包。
    • 此时,发送端重传丢失的包X(重传优先于发送新数据)。prr_out增加1,pipe不变(因为重传替代了发送)。
  • 时刻T2(收到新ACK,确认了刚重传的包X以及后续一些包,假设总共确认了5个MSS)
    • prr_delivered增加5。
    • 模式:可能仍在PRR=SS。
    • limit = (0+1+5) * 10 / 20 - 1 = 6*0.5 -1 = 3 -1 = 2。现在有2个包的发送信用。
    • pipe因为确认了5个包而减少5,假设现在pipe=15cwnd是10,但pipe > cwnd是允许的,这是恢复阶段的特点。只要pipe < cwnd + 信用之类的条件(具体看实现),且有信用,就可以发送。
    • 发送端可以发送2个新数据包(序列号在recover之后)。prr_out再增加2,pipe增加2变为17。
  • 持续过程:随着ACK陆续到达,prr_delivered增长,limit被持续计算。PRR=SS模式保证了总发送量prr_out大约是总交付量prr_delivered的一半。最终,当所有旧数据(序列号<recover)都被确认后,prr_delivered将等于RecoverFS(20)。此时,prr_out将被限制在20 * 10/20 = 10左右。同时,pipe也将逐渐下降到接近新的ssthresh(10)。
  • 恢复结束:当收到大于等于recover的ACK(即所有旧数据确认完毕),TCP退出快速恢复阶段,将cwnd设置为min(ssthresh, FlightSize + 1)等,进入拥塞避免阶段。此时发送速率已经平稳地过渡到了新的、减半后的窗口水平。

三、 总结与意义

  • PRR的目标:实现快速恢复阶段发送速率的平滑、按比例削减,避免突发流量。
  • 核心机制:通过追踪进入恢复后的交付量(prr_delivered)和发送量(prr_out),并强制它们保持(ssthresh / 原始FlightSize)的比例(在PRR=SS模式下),来精确控制发送节奏。
  • 效果:相比传统方法,PRR能减少恢复期间的包突发(burst),降低因突发导致的额外丢包或队列堆积风险,从而提升网络稳定性和整体吞吐量。它是对TCP快速恢复阶段发送引擎的一个重要优化。
TCP的拥塞控制算法中的PRR(Proportional Rate Reduction)机制详解 一、 问题/知识点描述 PRR是一种在TCP快速恢复阶段,用于精确控制数据发送速率,以更快、更平稳地从丢包中恢复的算法。它于2011年被提出,并被集成到Linux内核的TCP实现中,旨在替代传统快速恢复中较为粗糙的速率控制方法。 核心问题 :传统的快速恢复算法(如Reno、NewReno)在收到部分ACK(例如,触发快速重传的3个重复ACK)后,会将拥塞窗口(cwnd)减半,并进入恢复阶段。在这个阶段,发送端每收到一个ACK,就可以发送新数据。这可能导致发送速率突然下降后又突然飙升(因为cwnd减半但实际可发送量可能瞬时较多),造成发送“突发”(burst),加剧网络拥塞或导致不必要的丢包。PRR的目标是让发送速率在恢复阶段 平滑地、按比例地 降低到新的、减半后的cwnd值,并保持在这个水平,直到恢复结束。 二、 循序渐进的讲解 步骤1:场景设定与基础回顾 假设TCP连接正在正常传输,其 cwnd (拥塞窗口)为20个报文段(MSS), ssthresh (慢启动阈值)为15。 发生了一个数据包丢失。接收端因为后续数据包的到达,会发送重复的ACK(Dup-ACK)。 发送端收到第3个Dup-ACK,触发 快速重传 。此时,按照经典算法(如NewReno): ssthresh 更新为: ssthresh = max(在外数据包数 (FlightSize) / 2, 2) 。假设 FlightSize 为20,则 ssthresh = 10 。 cwnd 更新为: cwnd = ssthresh + 3 (因为收到了3个Dup-ACK,相当于“信用”)。所以 cwnd = 13 。 进入 快速恢复 阶段。 步骤2:传统快速恢复的发送行为(作为对比) 在快速恢复阶段,传统算法(如NewReno)的发送规则是: 每收到一个ACK(无论是重复ACK还是新数据的ACK),就可以发送一个新数据包 。 问题 :当 cwnd 从20骤降到13后,但 FlightSize (在外数据包数)可能因为重传和新发送而迅速变化。这个规则可能导致发送端在恢复初期,因为收到多个Dup-ACK,连续发送多个数据包,形成一个小的“突发流”。虽然 cwnd 限制了总量,但发送节奏不均匀。 步骤3:PRR算法的核心思想 PRR的核心是: 在快速恢复阶段,发送的数据总量应被精确控制,使得在恢复过程结束时,在外数据包数( FlightSize )恰好等于新的、减半后的拥塞窗口( ssthresh ) 。并且,这个发送过程应该是 按比例均匀分布 的。 目标 : prr_delivered (进入恢复阶段后成功递送的数据量)与 prr_out (进入恢复阶段后发送的数据量)保持一个恒定的比例,直到恢复完成。 公式核心 :它定义了在恢复期间, 每确认一个旧数据包,允许发送多少新数据包 。 步骤4:PRR算法的两个模式与关键变量 PRR定义了两个模式:PRR=SS(慢启动风格)和PRR=CRB(保守速率削减)。模式的选择基于一个关键变量: recover 。 变量初始化 (当触发快速重传,进入恢复阶段时): prr_delivered = 0 (进入恢复后,累计确认交付的字节数) prr_out = 0 (进入恢复后,累计发送的字节数) ssthresh = 当前计算的新的慢启动阈值(如步骤1中的10) pipe = 当前估算的在外数据包数(一个动态值,每发送+1,每确认-1)。 recover = snd.nxt (下一个要发送的序列号) 。这是恢复阶段的“终点”标志。所有序列号小于 recover 的数据,都属于这次恢复要处理的范围。 模式选择 (在每次决定能否发送数据时): 计算 prr_delivered 自恢复开始以来的增量。 如果当前 snd.nxt (下一个要发送的序列号) < recover ,说明我们还在重传或发送“旧”的、在恢复范围内的数据。此时使用 PRR=SS 模式。 如果当前 snd.nxt >= recover ,说明所有旧数据都已重传或发送完毕,开始发送全新的数据。此时使用 PRR=CRB 模式。通常PRR=CRB允许更保守的发送速率。 步骤5:PRR发送量计算(核心算法) 每当一个数据包被确认(ACK到达),算法计算本次可以发送多少数据: 更新 prr_delivered :增加刚被确认的字节数。 计算允许发送量 : PRR=SS模式(慢启动风格) : limit = prr_delivered * ssthresh / RecoverFS - prr_out 其中 RecoverFS 是进入恢复瞬间的 FlightSize (例子中的20)。 这个公式确保发送总量 prr_out 与交付总量 prr_delivered 的比例为 ssthresh/RecoverFS (例子中是10/20=0.5)。即 每交付2个字节,只允许发送1个新字节 ,从而平滑地将管道中的数据量从20削减到10。 PRR=CRB模式(保守速率削减) : limit = (prr_delivered - prr_out) 加上一些微调。 这个模式更严格,基本保证 prr_out 不会超过 prr_delivered 太多,确保恢复后 FlightSize 稳定在 ssthresh 附近,不会增长。 发送决策 :计算出的 limit 表示当前允许发送的字节数“信用”。只有当 limit > 0 且当前 pipe (估算在途数据)小于 cwnd (注意,在PRR中, cwnd 在恢复阶段通常被冻结为 ssthresh ,不用于直接限制发送,而是作为 pipe 的上界)时,才能发送新数据包。 步骤6:结合示例说明流程 接步骤1: cwnd_new = ssthresh = 10 , RecoverFS = 20 。 时刻T0(进入恢复) : prr_delivered=0 , prr_out=0 , pipe≈20 , snd.nxt 指向丢失的包(序列号X)。 recover 设为当前 snd.nxt 。 时刻T1(收到一个Dup-ACK,确认了序列号X-1的包) : prr_delivered 增加1个MSS。 模式: snd.nxt (X) < recover -> PRR=SS。 limit = 1 * 10 / 20 - 0 = 0.5 。由于不能发送半个包,通常实现会累积信用。假设信用足够发1个包。 此时,发送端 重传 丢失的包X(重传优先于发送新数据)。 prr_out 增加1, pipe 不变(因为重传替代了发送)。 时刻T2(收到新ACK,确认了刚重传的包X以及后续一些包,假设总共确认了5个MSS) : prr_delivered 增加5。 模式:可能仍在PRR=SS。 limit = (0+1+5) * 10 / 20 - 1 = 6*0.5 -1 = 3 -1 = 2 。现在有2个包的发送信用。 pipe 因为确认了5个包而减少5,假设现在 pipe=15 。 cwnd 是10,但 pipe > cwnd 是允许的,这是恢复阶段的特点。只要 pipe < cwnd + 信用 之类的条件(具体看实现),且有信用,就可以发送。 发送端可以发送2个新数据包(序列号在 recover 之后)。 prr_out 再增加2, pipe 增加2变为17。 持续过程 :随着ACK陆续到达, prr_delivered 增长, limit 被持续计算。PRR=SS模式保证了总发送量 prr_out 大约是总交付量 prr_delivered 的一半。最终,当所有旧数据(序列号< recover )都被确认后, prr_delivered 将等于 RecoverFS (20)。此时, prr_out 将被限制在 20 * 10/20 = 10 左右。同时, pipe 也将逐渐下降到接近新的 ssthresh (10)。 恢复结束 :当收到大于等于 recover 的ACK(即所有旧数据确认完毕),TCP退出快速恢复阶段,将 cwnd 设置为 min(ssthresh, FlightSize + 1) 等,进入拥塞避免阶段。此时发送速率已经平稳地过渡到了新的、减半后的窗口水平。 三、 总结与意义 PRR的目标 :实现快速恢复阶段发送速率的 平滑、按比例削减 ,避免突发流量。 核心机制 :通过追踪进入恢复后的交付量( prr_delivered )和发送量( prr_out ),并强制它们保持 (ssthresh / 原始FlightSize) 的比例(在PRR=SS模式下),来精确控制发送节奏。 效果 :相比传统方法,PRR能减少恢复期间的包突发(burst),降低因突发导致的额外丢包或队列堆积风险,从而提升网络稳定性和整体吞吐量。它是对TCP快速恢复阶段 发送引擎 的一个重要优化。