tcp 的 tlp, er 和 rack

tcp 协议曾引入过很多碎点,因此拧巴复杂,今天简单说两个,tlp(tail loss probe) 和 er(early retransmit)。

tlp 考虑的是怕发出去的报文太少或尾丢,没有足够的 ack/sack 数量足以触发快速重传,这种情况下需要主动发送一个报文(新的或旧的)以期待 receiver 回复的 ack 中携带足够的 sack 段触发快速重传。

而 er 考虑的是即使在发出的报文反馈的 ack/sack 不足以触发快速重传时,也不用怕,放宽约束即可在不足 3 个重复确认时触发快速重传。(3 个 dupack 中的 3 涉及到 reordering,但这是另一个话题,以前说过,不再赘述)

一般而言 tlp 和 er 一起使用,让这些特性显得复杂的一逼,如果看代码的话,一层层缩进的 if 混杂着一坨坨屎一样的 &&,& 和 || 就更让人绝望了。

细节中还有细节的细节,tlp 发新数据还是旧数据,若不明白 tlp 的初衷是希望 receiver 反馈 sack block 触发快速重传,很难理解发什么以及发多少的决策,其实发任意 byte 即可,考虑到捎带的有效性,优先发送新数据。

tlp 的 probe 报文大概率也带不回足量的 sack block,否则早就带回了,也不必触发 tlp 就开始快速重传了,所以如果一个 probe 仅仅带回了有但不足的 sack block,er 便接力,让 sender 在松约束下立即开始快速重传。

如果你花了很长时间来 "牢记" 相关 rfc 的约定和代码实现的 trick,并且你真的做到了,你甚至总结出一些非常容易记忆的 1,2,3,4,你也不必因此而感动,因为这些都不是核心本质,这只让 tcp 的形象越来越拧巴。

仔细看 tlp,er 这一坨坨的规定,很容易发现问题的核心在 "3 次 dupack 才能触发快速重传"。所以为了在一些不太易的场景(比如 thin 管道,尾丢)下迎合和满足触发快速重传的条件,不至于跌入 rto,tlp 和 er 才被设计出来,条件苛刻,代码凌乱。

所以你看,tcp 说在长肥管道下有问题,那么在短瘦管道下岂不是也有问题,tlp,er 就是为此而生的。

我常说,tcp 的代码写得像坨屎不是程序员的问题,而是 tcp 的代码根本就写不好,tcp 的特性是一点点小问题的解决 trick 逐渐像贴胶布一样贴出来的,换谁上也写不好看,这个和 openssl 还不一样。即使不说代码,你去看 rfc 也不好看。

rack 似乎是福音,因为它直接解除了 "3 次 dupack 才能触发快速重传" 这个条件,每当有人咨询我重传的问题时,我都会说你去看 rack 好了。

看 rack 之前的快速重传条件:

c 复制代码
static bool tcp_time_to_recover(struct sock *sk, int flag)
{
        struct tcp_sock *tp = tcp_sk(sk);
        __u32 packets_out;

        /* Trick#1: The loss is proven. */
        if (tp->lost_out)
                return true;

        /* Not-A-Trick#2 : Classic rule... */
        if (tcp_dupack_heuristics(tp) > tp->reordering)
                return true;

        /* Trick#4: It is still not OK... But will it be useful to delay
         * recovery more?
         */
        packets_out = tp->packets_out;
        if (packets_out <= tp->reordering &&
            tp->sacked_out >= max_t(__u32, packets_out/2, sysctl_tcp_reordering) &&
            !tcp_may_send_now(sk)) {
                /* We have nothing to send. This connection is limited
                 * either by receiver window or by application.
                 */
                return true;
        }

        /* If a thin stream is detected, retransmit after first
         * received dupack. Employ only if SACK is supported in order
         * to avoid possible corner-case series of spurious retransmissions
         * Use only if there are no unsent data.
         */
        if ((tp->thin_dupack || sysctl_tcp_thin_dupack) &&
            tcp_stream_is_thin(tp) && tcp_dupack_heuristics(tp) > 1 &&
            tcp_is_sack(tp) && !tcp_send_head(sk))
                return true;

        /* Trick#6: TCP early retransmit, per RFC5827.  To avoid spurious
         * retransmissions due to small network reorderings, we implement
         * Mitigation A.3 in the RFC and delay the retransmission for a short
         * interval if appropriate.
         */
        if (tp->do_early_retrans && !tp->retrans_out && tp->sacked_out &&
            (tp->packets_out >= (tp->sacked_out + 1) && tp->packets_out < 4) &&
            !tcp_may_send_now(sk))
                return !tcp_pause_early_retransmit(sk, flag);

        return false;
}

再看下 rack 后的:

c 复制代码
static bool tcp_time_to_recover(struct sock *sk, int flag)
{
        struct tcp_sock *tp = tcp_sk(sk);

        /* Trick#1: The loss is proven. */
        if (tp->lost_out)
                return true;

        /* Not-A-Trick#2 : Classic rule... */
        if (!tcp_is_rack(sk) && tcp_dupack_heuristics(tp) > tp->reordering)
                return true;

        return false;
}

很多逻辑都收入 rack 逻辑了,这就清爽了许多。

rack 是一个非常自然的丢包探测方案,包括我自己在内的传输圈子内部早在 2013 年之前就都自行设计出了时间序探测算法,后来看了 tlp 和 er 反而有点懵,因为它们是为不合理打补丁,而不是为问题提供方案。

本质上,rack 在跟踪时间序中的传输行为,它跟踪的正是时间戳本身,"更晚传输的报文 p 已经被确认,在 p 之前更早传输的未确认报文 pj 视为丢失",为了给乱序保留余地,rack 保留了一个乱序时间窗口 d,在标记并重传一个 pj 之前会等待一小段时间。乱序时间窗口 d 自然也不必像 reordering 个 dupack 触发 fr 时那般复杂拧巴,根据 "乱序是路径属性而不是协议属性",d 被限制在 minrtt / 4 到 srtt 之间,完全取消了复杂的乱序度的计算。这就把 rack 全部看懂了。

但同样也是时间序,很多人不明白 tcp 的 rto 为什么不能直接是 rtt,至多加上一个不太大的余量,这很合理,超过 rtt + 小余量 的时间收不到 ack,报文就算丢了,但当获得了更官方的解释后,捎带 ack,delay ack,各平台相异的实现等一大坨一大坨的东西就全搅和进来成一锅粥了。

rack 在标记一个报文丢失时与 rto 估算不同,rack 不需要估算,它用一个 recent ack 做参照,人家都被确认了,你们更早发送的有什么理由不被确认,那就是丢了呗。你看,有了 recent ack 参照就不需要再考虑那一坨坨的污秽逻辑了。

最后看一下 rack-tlp,来自 rfc8985,一张图,不说话:

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

相关推荐
ZZZCY20031 小时前
华为ENSP--IP编址及静态路由配置
网络·华为
EasyCVR1 小时前
私有化部署视频平台EasyCVR宇视设备视频平台如何构建视频联网平台及升级视频转码业务?
大数据·网络·音视频·h.265
hgdlip2 小时前
主IP地址与从IP地址:深入解析与应用探讨
网络·网络协议·tcp/ip
珹洺2 小时前
C语言数据结构——详细讲解 双链表
c语言·开发语言·网络·数据结构·c++·算法·leetcode
今天我刷leetcode了吗2 小时前
docker 配置同宿主机共同网段的IP 同时通过通网段的另一个电脑实现远程连接docker
tcp/ip·docker·电脑
科技象限2 小时前
电脑禁用U盘的四种简单方法(电脑怎么阻止u盘使用)
大数据·网络·电脑
东方隐侠安全团队-千里2 小时前
网安瞭望台第3期:俄黑客 TAG - 110组织与密码攻击手段分享
网络·chrome·web安全·网络安全
云计算DevOps-韩老师2 小时前
【网络云计算】2024第47周-每日【2024/11/21】周考-实操题-RAID6实操解析2
网络·云计算
lwprain3 小时前
安装支持ssl的harbor 2.1.4 docker 19.03.8 docker-compose 1.24.0
网络协议·ssl·harbor
软件技术员3 小时前
Let‘s Encrypt SSL证书:acmessl.cn申请免费3个月证书
服务器·网络协议·ssl