zoukankan      html  css  js  c++  java
  • tcpack--3快速确认模式- ack状态发送&清除

    ACK发送状态的转换图

     ACK的发送状态清除

    当成功发送ACK时,会删除延迟确认定时器,同时清零ACK的发送状态标志icsk->icsk_ack.pending

    ACK发送事件主要做了:更新快速确认模式中的ACK额度,删除ACK延迟定时器,清零icsk->icsk_ack.pending。

    在快速确认模式中,可以发送的ACK数量是有限制的,具体额度为icsk->icsk_ack.quick。当额度用完时,就进入延迟确认模式。

    static int tcp_transmit_skb (struct sock *sk, struct sk_buff *skb, int clone_it, gfp_t gfp_mask)
    {
        ------------------------------
        if (likely(tcb->tcp_flags & TCPHDR_ACK))
            tcp_event_ack_sent(sk, tcp_skb_pcount(skb)); /* ACK发送事件的处理 */
        ------------------------------------------------
    }
    //ACK发送事件主要做了:更新快速确认模式中的ACK额度,删除ACK延迟定时器,清零icsk->icsk_ack.pending。
    /* Account for an ACK we sent. */
    static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts)
    {
        tcp_dec_quickack_mode(sk, pkts);// 更新快速确认模式的ACK额度
        inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);//删除ACK延迟定时器
    }
    static inline void tcp_dec_quickack_mode (struct sock *sk, const unsigned int pkts)
    {
        struct inet_connection_sock *icsk = inet_csk(sk);
     
        if (icsk->icsk_ack.quick) { /* 如果额度不为0 */
            if (pkts >= icsk->icsk_ack.quick) {
                icsk->icsk_ack.quick = 0;
                /* Leaving quickack mode we deflate ATO. */
                icsk->icsk_ack.ato = TCP_ATO_MIN;
            } else
                icsk->icsk_ack.quick -= pkts;
        }

    快速确认:

    • tcp_enter_quickack_mode: 进入快速确认模式,设置快速确认模式标志,设置在快速确认模式中可以发送的ACK数量
    /*
    在快速确认模式中,可以发送的ACK数量是有限制的,具体额度为icsk->icsk_ack.quick。
    所以进入快速确认模式时,需要设置可以快速发送的ACK数量,一般允许快速确认半个接收窗口的数据量,但最多不能超过16个,最少为2个。
    */
    static void tcp_enter_quickack_mode (struct sock *sk)
    {
        struct inet_connection_sock *icsk = inet_csk(sk);
     
        tcp_incr_quickack(sk); /* 设置在快速确认模式中可以发送的ACK数量 */
        icsk->icsk_ack.pingpong = 0; /* 快速确认模式的标志 */
        icsk->icsk_ack.ato = TCP_ATO_MIN; /* ACK超时时间 */
    
    }

    static void tcp_incr_quickack(struct sock *sk)
    {/* Maximal number of ACKs sent quickly to accelerate slow-start. */
    #define TCP_MAX_QUICKACKS    16U
        struct inet_connection_sock *icsk = inet_csk(sk);
        unsigned int quickacks = tcp_sk(sk)->rcv_wnd / (2 * icsk->icsk_ack.rcv_mss);
    
        if (quickacks == 0)
            quickacks = 2;
        if (quickacks > icsk->icsk_ack.quick)
            icsk->icsk_ack.quick = min(quickacks, TCP_MAX_QUICKACKS);
    }
    • tcp_in_quickack_mode:检查是否处于快速确认模式。如果设置了快速确认标志,且快速确认模式中可以发送的ACK数量不为0,就判断连接处于快速确认模式中
    /* Send ACKs quickly, if "quick" count is not exhausted
     * and the session is not interactive.
     */
    
    static bool tcp_in_quickack_mode(struct sock *sk)
    {
        const struct inet_connection_sock *icsk = inet_csk(sk);
        const struct dst_entry *dst = __sk_dst_get(sk);
    //如果快速确认模式中可以发送的ACK数量不为0,且设置了快速确认标志 且 dst 路由存在
        return (dst && dst_metric(dst, RTAX_QUICKACK)) ||
            (icsk->icsk_ack.quick && !icsk->icsk_ack.pingpong);
    }
    • 在 tcp_rcv_state_process 以及tcp_rcv_established中 处理完接收到的报文处理完之后;会调用_tcp_ack_snd_check()来发送快速确认或延迟确认

    tcp_ack_snd_check()会检查是否需要发送ACK,以及是使用快速确认还是延迟确认。

    /*
    static inline int inet_csk_ack_scheduled(const struct sock *sk)
    {
        return inet_csk(sk)->icsk_ack.pending & ICSK_ACK_SCHED;
    }
    
    */
    static inline void tcp_ack_snd_check(struct sock *sk)
    {
        if (!inet_csk_ack_scheduled(sk)) {//如果没有ACK需要发送
            /* We sent a data segment already. */
            return;
        }
        __tcp_ack_snd_check(sk, 1);
    }

    对于以下情况可以立即发送ACK,即进行快速确认:

    1. 接收缓冲区中有一个以上的全尺寸数据段仍然是NOT ACKed,并且接收窗口变大了。所以一般收到了两个数据包后,会发送ACK,而不是对每个数据包都进行确认。

    2.  此时处于快速确认模式中。

    3. 乱序队列不为空。

    /*
     * Check if sending an ack is needed.
     */
    static void __tcp_ack_snd_check(struct sock *sk, int ofo_possible)
    {
        struct tcp_sock *tp = tcp_sk(sk);
    
            /* More than one full frame received... */
        if (((tp->rcv_nxt - tp->rcv_wup) > inet_csk(sk)->icsk_ack.rcv_mss &&
             /* ... and right edge of window advances far enough.
              * (tcp_recvmsg() will send ACK otherwise). Or...
              */
             __tcp_select_window(sk) >= tp->rcv_wnd) ||
            /* We ACK each frame or... */
            tcp_in_quickack_mode(sk) ||
            /* We have out of order data. */
            (ofo_possible && !RB_EMPTY_ROOT(&tp->out_of_order_queue))) {
            /* Then ack it now ACK的发送函数为tcp_send_ack(),如果发送失败会启动ACK延迟定时器。 */
            tcp_send_ack(sk);
        } else {
            /* Else, send delayed ack. */
            tcp_send_delayed_ack(sk);
        }
    }

    TCP_QUICKACK选项

    TCP_QUICKACK用于让本端立即发送ACK,而不进行延迟确认。需要注意的是,这个选项并不是可持续的,之后还是有可能进入延迟确认模式的。

    所以如果需要一直进行快速确认,要在每次调用接收函数后都进行选项设置。

  • 相关阅读:
    〖Linux〗秒开www.stackoverflow.com,非代理方式
    〖Linux〗git push orgin master不能解析域名的解决方法
    unity, terrain道出为obj
    unity, 顶点对齐
    world machine, 输出lightmap
    unity, scene视图查看场景时应调成正交模式
    unity, 由scriptableObject创建.asset
    unity, 播放循环背景音乐注意事项
    用audacity制作循环背景音乐
    unity, 保存prefab时material丢失问题
  • 原文地址:https://www.cnblogs.com/codestack/p/11927988.html
Copyright © 2011-2022 走看看