TCP擁塞控制算法,以及長肥管道

linux內核裏面擁塞控制算法比較多,目前大部分用的cubic算法。

在內核裏面代碼是怎樣實現的呢?

static struct tcp_congestion_ops cubictcp __read_mostly = {
    .init        = bictcp_init,  初始化
    .ssthresh    = bictcp_recalc_ssthresh,   更新tcp的ssthresh值
    .cong_avoid    = bictcp_cong_avoid,    重新計算cwnd值
    .set_state    = bictcp_state,                  ca狀態更新
    .undo_cwnd    = tcp_reno_undo_cwnd,   丟包時候cwnd更新處理
    .cwnd_event    = bictcp_cwnd_event,    當cwnd事件發生時調用的
    .pkts_acked     = bictcp_acked,         數據包ack計數
    .owner        = THIS_MODULE,
    .name        = "cubic",
};
看下大概回調怎麼實現的

static void bictcp_init(struct sock *sk)
{
    struct bictcp *ca = inet_csk_ca(sk);

    bictcp_reset(ca);

    if (hystart)
        bictcp_hystart_reset(sk);

    if (!hystart && initial_ssthresh)
        tcp_sk(sk)->snd_ssthresh = initial_ssthresh;
}//主要是 ca初始化和ssthresh值初始化

出現擁塞是重新計算ssthresh,就是當前cwnd的一半

static u32 bictcp_recalc_ssthresh(struct sock *sk)
{
    const struct tcp_sock *tp = tcp_sk(sk);
    struct bictcp *ca = inet_csk_ca(sk);

    ca->epoch_start = 0;    /* end of epoch */

    /* Wmax and fast convergence */
    if (tp->snd_cwnd < ca->last_max_cwnd && fast_convergence)
        ca->last_max_cwnd = (tp->snd_cwnd * (BICTCP_BETA_SCALE + beta))
            / (2 * BICTCP_BETA_SCALE);
    else
        ca->last_max_cwnd = tp->snd_cwnd;

    return max((tp->snd_cwnd * beta) / BICTCP_BETA_SCALE, 2U);
}

static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
{
    struct tcp_sock *tp = tcp_sk(sk);
    struct bictcp *ca = inet_csk_ca(sk);

    if (!tcp_is_cwnd_limited(sk))
        return;

    if (tcp_in_slow_start(tp)) { //分爲正在慢啓動過程中的計算
        if (hystart && after(ack, ca->end_seq))
            bictcp_hystart_reset(sk);
        acked = tcp_slow_start(tp, acked);
        if (!acked)
            return;
    }
    bictcp_update(ca, tp->snd_cwnd, acked);
    tcp_cong_avoid_ai(tp, ca->cnt, acked);   //正常窗口值 ++
}

u32 tcp_reno_undo_cwnd(struct sock *sk) //丟包時的處理
{
    const struct tcp_sock *tp = tcp_sk(sk);

    return max(tp->snd_cwnd, tp->prior_cwnd);
}

cubic算法的整體邏輯

TCP的四種擁塞控制算法
1.慢開始   窗口大小達到ssthresh之後慢開始
2.擁塞控制   判斷爲擁塞避免的時候,ssthresh馬上減半
3.快重傳   當出現3個重複的ack的時候,馬上重傳後面一個分組。如下圖


4.快恢復  開始的時候指數級恢復

長肥管道:

有了上面理論基礎之後看下面的問題,當網絡的RTT非常大,已知光速大概30wkm/s,如果是跨國網絡,距離非常遠。RTT很大,這樣一來。來回的ack時間就很長,如果出現丟包馬上就會變成ssthresh減半,慢啓動狀態。窗口恢復非常困難。

tcp 窗口大小就會是這樣的曲線

如何解決這種問題。

調大接收端的tcp_rmem 中間那個缺省值,

echo "4096    52420000  62910000" > /proc/sys/net/ipv4/tcp_rmem

然後再抓包發現初始化的接收窗口確實變大了,用wireshark看起來沒有變大是因爲忽略了後面的WS字段,這個是窗口放大倍數,所以長肥管道爲了保證有足夠的數據在通信信道上跑,就要把接收端的recv buf設置非常大。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章