📜  比例速率降低 - TCP 丢失恢复算法

📅  最后修改于: 2022-05-13 01:56:13.273000             🧑  作者: Mango

比例速率降低 - TCP 丢失恢复算法

TCP CUBIC 正在成为 Linux 内核中的默认设置,而用于避免拥塞的速率减半技术正在成为 Linux 内核中的默认设置。所以,这两者之间存在不匹配。因为速率减半对 TCP Reno、Tahoe 和 NewReno 有利。这对那些仅在发生丢包时将 cwnd 减少一半的 TCP 是有益的。但与其他 TCP 不同,TCP Cubic 将 cwnd 降低了 30%。因此,Rate Halving 和 CUBIC 不能作为 Linux 内核的默认设置。这就是谷歌的动机,他们发明了一种新的算法,称为比例降频。这在 RFC 6937 中有介绍。

比例降低:

PRR 使用一些公式计算 ACK 到达时要发送的新数据包的数量。为这些方程创造了一些新术语,新术语如下:

1. sndcnt (读作“发送计数”) :在 DupACK 到达时要发送的数据量。这是发送方在新 ACK 到达时发送的新数据包的数量。与快速恢复不同,它不等于 2,并且与速率减半不同,它不会在两个 DUP-ACK 到达时发送 1 个新数据包。它可以发送使用公式计算的任意数量的新数据包。

2RecoverFS:恢复阶段开始时pipe的值

3. prr_delivered: “在”恢复阶段交付的数据量。这是自恢复阶段开始以来到目前为止传送给接收器的累积数据的计数。这等于在恢复阶段到达的 DUP-ACK 的数量。在恢复阶段不重要之前传递了多少数据包?它只计算丢包后传递的数据包。

4. prr_out: “在”恢复阶段传输的数据量。这会计算发送方进入恢复阶段后发送的新数据包的累积数量。

5. DeliveredData:由 ACK 数据包指示的数据量(查看序列号)。不要将此与“prr_delivered”混淆。 DeliveredData 表示此 DUP_ACK 传送的数据包数量。如果 ACK 无序到达,它可以是 1 或更多。

6.limit 数据发送限制(在PRR算法中计算)。这可以被认为是阈值。它在方程式内部使用并在中间步骤中计算。

7. MSS:最大段大小。这是一个段的典型大小。由于我们将使用所有术语的粒度作为段,因此 MSS 变为 1。

比例降息方式:

Proportional Rate Reduction (PRR) 有两种模式,分别是:

  1. 保守还原界限 (CRB)
  2. Slow Start Reduction Bound (SSRB) [这个在 Linux 中默认开启]

我们一次只能使用一种模式,两种模式不能同时使用。

PRR的工作:

当恢复阶段开始时,管道值大于 cwnd。但是可能会出现 SACK 块确认数据包丢失并且管道值变得小于 cwnd 的情况。 PRR 算法以不同方式处理所有这些情况。

案例1:PRR(案例:管道> ssthresh)

当发件人进入恢复阶段时:

pipe = 10 segments, 
RecoverFS = 10 segments
ssthresh = 7 segments,

cwnd 由 PRR 计算:

cwnd = pipe + sndcnt
sndcnt = CEIL(p_d * ssthresh / RecoverFS) - p_o, {p_d=prr_delivered, p_o=prr_out}

Step 1 :一个DupACK来了,pipe=10-1=9, p_d=1, p_o=0, (Pipe减一) 。 DupACK 表示已交付 1 个数据包。所以, p_d=1 ,还没有发送新的数据包,所以p_o=0

sndcnt= CEIL(1*7/10)-0 = CEIL(0.7)-0 = 1
sender sends one new packet and pipe becomes =9+1=10 again.

Step 2 :一个DupACK来了,pipe=10-1=9,p_d=2,p_o=1, (Pipe减一)。 DupACK 表示已发送一个数据包,因此p_d=2 (累积)。已经发送了一个新数据包,因此p_o=1。

sndcnt= CEIL(2*7/10)-1 = CEIL(1.4)-1= 1
sender sends one new packet and pipe becomes 9 + 1 = 10 again.

这就是它继续的方式。

PRR(案例:管道> ssthresh)” srcset =” https://media.geeksforgeeks.org/wp-content/uploads/20220225160117/prrcase12.jpg 509w,https://media.geeksforgeeks.org/wp-content/uploads /20220225160117/prrcase12-100×94.jpg 100w,https://media.geeksforgeeks.org/wp-content/uploads/20220225160117/prrcase12-200×188.jpg 200w,https://media.geeksforgeeks.org/wp -content/uploads/20220225160117/prrcase12-300×282.jpg 300w” sizes=”100vw” width=”509″></figure> <h4 style=执行:

下面是案例一的代码实现:

C++
// pipe>cwnd, PRR
// PRR, case-1
#include 
using namespace std;
  
// utility function
void prr(float pipe, float cwnd, float recoverFS,
         float ssthresh)
{
    int i;
    float prr_delivered = 0, prr_out = 0, snd_cnt;
  
    for (i = 1;; i++) {
        // one DUP-ACK arrived
        pipe -= 1;
  
        // terminating condition.
        if (pipe < ssthresh)
            break;
  
        // Calculation
        cout << "Iteration " << i << " => "
             << "pipe= " << pipe;
        prr_delivered = i;
        cout << " p_d= " << prr_delivered;
        snd_cnt = ceil(prr_delivered * ssthresh / recoverFS)
                  - prr_out;
        cout << " snd_cnt= " << snd_cnt;
        cout << " p_o= " << prr_out << "\n";
        prr_out += snd_cnt;
        cwnd = pipe + snd_cnt;
        pipe = cwnd;
    }
}
  
// main function
int main()
{
    float pipe, cwnd, recoverFS, ssthresh;
    pipe = 10;
    ssthresh = 7;
    cwnd = pipe;
    recoverFS = pipe;
    prr(pipe, cwnd, recoverFS, ssthresh);
}


C++
// pipe
using namespace std;
  
// utility function
void prr_ssrb(float pipe, float cwnd, float recoverFS,
              float ssthresh)
{
    int i;
    float prr_delivered = 0, prr_out = 0, limit, snd_cnt;
  
    for (i = 1; i < 10; i++) {
        // one DUP-ACK arrived
        pipe -= 1;
  
        // Calculation
        cout << "Iteration " << i << " => pipe= " << pipe;
        prr_delivered = i;
        cout << " p_d= " << prr_delivered;
        cout << " p_o= " << prr_out;
        limit = max(prr_delivered - prr_out, float(1)) + 1;
        cout << " limit= " << limit;
        snd_cnt = min(ssthresh - pipe, limit);
        cout << " snd_cnt= " << snd_cnt << "\n";
        prr_out += snd_cnt;
  
        cwnd = pipe + snd_cnt;
        pipe = cwnd;
  
        // terminating condition.
        if (pipe > ssthresh)
            break;
    }
}
  
// main function
int main()
{
    float pipe, cwnd, recoverFS, ssthresh;
    pipe = 4, recoverFS = 10, ssthresh = 5;
    prr_ssrb(pipe, cwnd, recoverFS, ssthresh);
}


C++
// pipe
using namespace std;
  
// utility function
void prr_crb(float pipe, float cwnd, float recoverFS,
             float ssthresh)
{
    int i;
    float prr_delivered = 0, prr_out = 0, limit, snd_cnt;
  
    for (i = 1; i < 10; i++) {
        // one DUP-ACK arrived
        pipe -= 1;
  
        // Calculation
        cout << "Iteration " << i << " => pipe= " << pipe;
        prr_delivered = i;
        cout << " p_d= " << prr_delivered;
        cout << " p_o= " << prr_out;
        limit = prr_delivered - prr_out;
        cout << " limit= " << limit;
        snd_cnt = min(ssthresh - pipe, limit);
        cout << " snd_cnt= " << snd_cnt << "\n";
        prr_out += snd_cnt;
  
        cwnd = pipe + snd_cnt;
        pipe = cwnd;
  
        // terminating condition.
        if (pipe > ssthresh)
            break;
    }
}
  
// main function
int main()
{
    float pipe = 4, cwnd, recoverFS = 10, ssthresh = 5;
  
    prr_crb(pipe, cwnd, recoverFS, ssthresh);
}


输出
Iteration 1 => pipe= 9 p_d= 1 snd_cnt= 1 p_o= 0
Iteration 2 => pipe= 9 p_d= 2 snd_cnt= 1 p_o= 1
Iteration 3 => pipe= 9 p_d= 3 snd_cnt= 1 p_o= 2
Iteration 4 => pipe= 9 p_d= 4 snd_cnt= 0 p_o= 3
Iteration 5 => pipe= 8 p_d= 5 snd_cnt= 1 p_o= 3
Iteration 6 => pipe= 8 p_d= 6 snd_cnt= 1 p_o= 4
Iteration 7 => pipe= 8 p_d= 7 snd_cnt= 0 p_o= 5
Iteration 8 => pipe= 7 p_d= 8 snd_cnt= 1 p_o= 5
Iteration 9 => pipe= 7 p_d= 9 snd_cnt= 1 p_o= 6
Iteration 10 => pipe= 7 p_d= 10 snd_cnt= 0 p_o= 7

CASE 2: PRR (Case: pipe ≤ ssthresh, with SSRB)

当恢复阶段开始时:

pipe = 4 segments
RecoverFS = 10 segments
ssthresh = 5 segments

cwnd 由 PRR 计算:

cwnd = pipe + sndcnt
sndcnt = MIN (ssthresh - pipe, limit) and 
limit = MAX (p_d - p_o, DeliveredData) + MSS

步骤1:一个DupACK来了,pipe减1, pipe=4-1=3 ,DupACK确认1个包已经送达,所以p_d=1 ,还没有新包发送,所以p_o=0

limit = MAX(1-0, 1)+1 = 1+1 = 2
sndcnt= MIN(5-3, 2) = MIN(2, 2) = 2
So, the sender sends 2 new packets. Thus, pipe=3+2=5

第2步:一个DupACK来了,pipe减1, pipe=5-1=4 ,DupACK确认1个数据包已经送达,所以p_d=2(累计)一个新的数据包已经发送,所以p_o=1

limit = MAX(2-1, 1)+1 = 1+1 = 2
sndcnt= MIN(5-4, 2) = MIN(1, 2) = 1
So, the sender sends 1 new packet. Thus, pipe=4+1=5

这就是它继续的方式。

执行:

下面是 CASE 2 的代码实现:

C++

// pipe
using namespace std;
  
// utility function
void prr_ssrb(float pipe, float cwnd, float recoverFS,
              float ssthresh)
{
    int i;
    float prr_delivered = 0, prr_out = 0, limit, snd_cnt;
  
    for (i = 1; i < 10; i++) {
        // one DUP-ACK arrived
        pipe -= 1;
  
        // Calculation
        cout << "Iteration " << i << " => pipe= " << pipe;
        prr_delivered = i;
        cout << " p_d= " << prr_delivered;
        cout << " p_o= " << prr_out;
        limit = max(prr_delivered - prr_out, float(1)) + 1;
        cout << " limit= " << limit;
        snd_cnt = min(ssthresh - pipe, limit);
        cout << " snd_cnt= " << snd_cnt << "\n";
        prr_out += snd_cnt;
  
        cwnd = pipe + snd_cnt;
        pipe = cwnd;
  
        // terminating condition.
        if (pipe > ssthresh)
            break;
    }
}
  
// main function
int main()
{
    float pipe, cwnd, recoverFS, ssthresh;
    pipe = 4, recoverFS = 10, ssthresh = 5;
    prr_ssrb(pipe, cwnd, recoverFS, ssthresh);
}
输出
Iteration 1 => pipe= 3 p_d= 1 p_o= 0 limit= 2 snd_cnt= 2
Iteration 2 => pipe= 4 p_d= 2 p_o= 2 limit= 2 snd_cnt= 1
Iteration 3 => pipe= 4 p_d= 3 p_o= 3 limit= 2 snd_cnt= 1
Iteration 4 => pipe= 4 p_d= 4 p_o= 4 limit= 2 snd_cnt= 1
Iteration 5 => pipe= 4 p_d= 5 p_o= 5 limit= 2 snd_cnt= 1
Iteration 6 => pipe= 4 p_d= 6 p_o= 6 limit= 2 snd_cnt= 1
Iteration 7 => pipe= 4 p_d= 7 p_o= 7 limit= 2 snd_cnt= 1
Iteration 8 => pipe= 4 p_d= 8 p_o= 8 limit= 2 snd_cnt= 1
Iteration 9 => pipe= 4 p_d= 9 p_o= 9 limit= 2 snd_cnt= 1

案例3:PRR(案例:管道≤ssthresh,有CRB)

最初,当发件人进入恢复阶段时:

pipe = 4 segments
RecoverFS = 10 segments
ssthresh = 5 segments

cwnd 由 PRR 计算:

cwnd = pipe + sndcnt
sndcnt = MIN (ssthresh - pipe, limit), and 
limit = p_d - p_o

步骤1:一个DupACK到达,pipe减1, pipe=4-1=3 ,DupACK确认有一个包送达,所以p_d=1 ,还没有新包发送,所以p_o=0。

limit = 1-0 = 1,
sndcnt = MIN(5-3, 1) = MIN(2, 1) = 1
Sender sends one new packet, thus pipe = 3+1 = 4

Step 2:一个DupACK到达,pipe减1,

pipe= 4-1=3,
p_d=2, p_o=1
limit = 2-1 = 1
sndcnt = MIN(5-3, 1) = MIN(2, 1) = 1
Sender sends one new packet, thus pipe = 3+1 = 4

这就是它继续的方式。

PRR(案例:管道≤ssthresh,有CRB)

执行:

下面是 CASE 3 的代码实现:

C++

// pipe
using namespace std;
  
// utility function
void prr_crb(float pipe, float cwnd, float recoverFS,
             float ssthresh)
{
    int i;
    float prr_delivered = 0, prr_out = 0, limit, snd_cnt;
  
    for (i = 1; i < 10; i++) {
        // one DUP-ACK arrived
        pipe -= 1;
  
        // Calculation
        cout << "Iteration " << i << " => pipe= " << pipe;
        prr_delivered = i;
        cout << " p_d= " << prr_delivered;
        cout << " p_o= " << prr_out;
        limit = prr_delivered - prr_out;
        cout << " limit= " << limit;
        snd_cnt = min(ssthresh - pipe, limit);
        cout << " snd_cnt= " << snd_cnt << "\n";
        prr_out += snd_cnt;
  
        cwnd = pipe + snd_cnt;
        pipe = cwnd;
  
        // terminating condition.
        if (pipe > ssthresh)
            break;
    }
}
  
// main function
int main()
{
    float pipe = 4, cwnd, recoverFS = 10, ssthresh = 5;
  
    prr_crb(pipe, cwnd, recoverFS, ssthresh);
}
输出
Iteration 1 => pipe= 3 p_d= 1 p_o= 0 limit= 1 snd_cnt= 1
Iteration 2 => pipe= 3 p_d= 2 p_o= 1 limit= 1 snd_cnt= 1
Iteration 3 => pipe= 3 p_d= 3 p_o= 2 limit= 1 snd_cnt= 1
Iteration 4 => pipe= 3 p_d= 4 p_o= 3 limit= 1 snd_cnt= 1
Iteration 5 => pipe= 3 p_d= 5 p_o= 4 limit= 1 snd_cnt= 1
Iteration 6 => pipe= 3 p_d= 6 p_o= 5 limit= 1 snd_cnt= 1
Iteration 7 => pipe= 3 p_d= 7 p_o= 6 limit= 1 snd_cnt= 1
Iteration 8 => pipe= 3 p_d= 8 p_o= 7 limit= 1 snd_cnt= 1
Iteration 9 => pipe= 3 p_d= 9 p_o= 8 limit= 1 snd_cnt= 1