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,一张图,不说话:

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

相关推荐
ProcessOn官方账号13 分钟前
如何绘制网络拓扑图?附详细分类解说和用户案例!
网络·职场和发展·流程图·拓扑学
Ven%37 分钟前
如何在防火墙上指定ip访问服务器上任何端口呢
linux·服务器·网络·深度学习·tcp/ip
神的孩子都在歌唱1 小时前
TCP/IP 模型中,网络层对 IP 地址的分配与路由选择
网络·tcp/ip·智能路由器
阿雄不会写代码1 小时前
ubuntu安装nginx
linux·服务器·网络
starstarzz2 小时前
计算机网络实验四:Cisco交换机配置VLAN
网络·计算机网络·智能路由器·vlan·虚拟局域网
EasyDSS3 小时前
国标GB28181-2022平台EasyGBS:安防监控中P2P的穿透方法
网络协议·php·音视频·p2p
网安墨雨3 小时前
常用网络协议
网络·网络协议
Tlzns3 小时前
Linux网络——UDP的运用
linux·网络·udp
黑客老陈3 小时前
新手小白如何挖掘cnvd通用漏洞之存储xss漏洞(利用xss钓鱼)
运维·服务器·前端·网络·安全·web3·xss
HSunR5 小时前
计算机网络
网络·计算机网络