UDP 丢包的艺术

不像 TCP 内置端到端流控机制,无连接的 UDP 在 Buffer 满了之后,只能丢弃当前收到的数据包,这是一个常规操作。

但注意,如果这是一条 UDP 隧道,即 UDP 运载了 TCP 载荷,简单丢弃该 UDP 报文就是一个针对 TCP 的尾丢操作:
UDP 丢包的艺术_第1张图片

尾丢往往会持续,进而引发 TCP 超时,如果多流共享隧道,还可能全局同步。总之,尾丢至少会引起 TCP (以及同样使能 Loss-based cc 的 AIMD 流量,如 CUBIC-QUIC)载荷带宽随时间呈大锯齿形波动。

这个地方做个 RED(random early detection)/WRED(weighted random early detection) 就好了,但只针对隧道 UDP,可以通过 iptables 命令模拟:

iptables -I INPUT -p udp --dport $隧道服务端口  -m statistic --mode random --probability 0.005 -j DROP

但这显然无法体现随机丢包的阈值,大突发流量下以稳定换吞吐(额外的丢包率"有可能"降低平均吞吐)。
下面的 HOOK 函数挂在 NF_INET_LOCAL_IN 上将会很高尚:

static unsigned int red_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
        struct iphdr *iph;
        struct sock *sk;
        int rmem;
        unsigned long rand[1];

        iph = ip_hdr(skb);

        if (iph->protocol != IPPROTO_UDP)
                return NF_ACCEPT;

        if (!skb->sk) {
                return NF_ACCEPT;
        }
        sk = skb->sk;
        rmem = atomic_read(&sk->sk_rmem_alloc);
        if (rmem > (sk->sk_rcvbuf - (sk->sk_rcvbuf >> 2))) {
                get_random_bytes(&rand[0], sizeof(unsigned long));
                if (rand[0] % 200 == 0) {
                        return NF_DROP;
                }
        }

        return NF_ACCEPT;
}

RED/WRED 不仅可用于 UDP 隧道,对于普通 UDP 流量,RED/WRED 替换尾丢的高尚之处在于:

  • 避开了连续丢包,统计分辨率和公平性更好。
  • 应用层若做拥塞控制,拥塞更容易被感知。

端主机的 UDP Buffer,是 UDP 端到端的最后一个拥塞点,就像它在链路上一样好咯。

全局同步,准全局同步( RTT 一般都不同)都不是高尚的,对文件传输虽慢点倒还无害,但对流媒体传输就是咔咔咔了,瞬时速率无征兆跌零,给人一种失控之感觉。祸首是 Buffer 尾丢,这种风格的丢包会导致同一连接连续丢包,这也是时间局部性和 Bursty style 的结果。把丢包损失在事前分担给所有流量,最廉价的方案就是随机,就是 RED 背后的思想。

浙江温州皮鞋湿,下雨进水不会胖。

你可能感兴趣的:(udp,网络,tcp/ip)