【问题标题】:Delayed ACK at receiver vs. RTO at sender接收方的延迟 ACK 与发送方的 RTO
【发布时间】:2016-02-13 18:53:54
【问题描述】:

如果H1打开到H2的TCP连接,那么H1发送一个小包(

TCP 如何确保 H1 的 RTO 计时器在收到延迟的 ACK 之前不会过期?

Linux默认最小RTO为200ms是否正确理解,所以如果网络很快(RTO保持在最小200ms)并且数据包在从H1到H2的途中丢失,那么H1会在200ms内重试?如果网络很慢,那么 H1 的等待时间可能会超过 200ms?

【问题讨论】:

    标签: c linux sockets tcp


    【解决方案1】:

    关于延迟的 ACK 时间,RFC 1122 说:

    一个 TCP 应该实现一个延迟的 ACK,但一个 ACK​​ 不应该 被过度延误;特别是,延迟必须是 不到 0.5 秒,并且在全尺寸的流中 段应该至少每秒有一个 ACK 段。

    所以它取决于实现,当然也取决于是否会降低应用程序性能。
    在 linux 内核中,它们不会按计时器或固定间隔发送延迟的 ACK,正如您在以下代码中所见,它们的行为因条件而异。 正如您在net/ipv4/tcp_input.c 中看到的,他们在评论中说:

    在分析数据时必须牢记一些事情 tp->ato 延迟确认超时间隔的行为。当一个 连接启动,我们希望尽快确认。这 问题是“好”的 TCP 在数据开始时启动缓慢 传播。这意味着在我们发送前几个 ACK​​ 之前 发件人将坐在他的一端,只排队他的大部分数据,因为 他只能在任何给定时间发送 snd_cwnd 未确认的数据包。为了 我们发送的每个 ACK​​,他都会增加 snd_cwnd 并传输更多他的 队列。 -戴夫M

    static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb)
    {
        struct tcp_sock *tp = tcp_sk(sk);
        struct inet_connection_sock *icsk = inet_csk(sk);
        u32 now;
    
        inet_csk_schedule_ack(sk);
    
        tcp_measure_rcv_mss(sk, skb);
    
        tcp_rcv_rtt_measure(tp);
    
        now = tcp_time_stamp;
    
        if (!icsk->icsk_ack.ato) {
            /* The _first_ data packet received, initialize
             * delayed ACK engine.
             */
            tcp_incr_quickack(sk);
            icsk->icsk_ack.ato = TCP_ATO_MIN;
        } else {
            int m = now - icsk->icsk_ack.lrcvtime;
    
            if (m <= TCP_ATO_MIN / 2) {
                /* The fastest case is the first. */
                icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + TCP_ATO_MIN / 2;
            } else if (m < icsk->icsk_ack.ato) {
                icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + m;
                if (icsk->icsk_ack.ato > icsk->icsk_rto)
                    icsk->icsk_ack.ato = icsk->icsk_rto;
            } else if (m > icsk->icsk_rto) {
                /* Too long gap. Apparently sender failed to
                 * restart window, so that we send ACKs quickly.
                 */
                tcp_incr_quickack(sk);
                sk_mem_reclaim(sk);
            }
        }
        icsk->icsk_ack.lrcvtime = now;
    
        tcp_ecn_check_ce(tp, skb);
    
        if (skb->len >= 128)
            tcp_grow_window(sk, skb);
    }
    

    【讨论】:

    • 嗯,实现是 linux(最新内核)。是的,我阅读了 RFC。
    • 你想知道在linux内核中实现的具体价值吗?
    • 我添加了一些来自 linux 内核的 cmets 和代码,希望对您有所帮助。
    • 谢谢。但是发件人的行为是什么?如果接收方没有确认,它在重新发送之前等待多长时间?整个 rto?
    • 这是TCP的一种输入行为,如果你想看net/ipv4/tcp_output.c,我想你可以在那里找到答案
    猜你喜欢
    • 1970-01-01
    • 2018-10-12
    • 2012-07-27
    • 2020-11-13
    • 1970-01-01
    • 2017-12-09
    • 1970-01-01
    • 2017-01-03
    • 2021-11-27
    相关资源
    最近更新 更多