不可靠不重传的假 TCP

Posted dog250

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了不可靠不重传的假 TCP相关的知识,希望对你有一定的参考价值。

还是那个面试题:如何用 TCP 实现不可靠传输?… 本文有趣,黑科技,仔细看。

TCP 是"运营商友好"的。运营商依据 TCP 状态机能以任意粒度对 TCP 连接进行任意管控,包括不限于限速,整形,重置,计费,TCP 和运营商是互惠的。

UDP 像脱缰野马变幻莫测,运营商只能一刀切,比如高峰期给所有 UDP 流量仅 10 % 的资源份额。

让运营商看起来是 TCP,但实际上是 UDP,可以考虑做一个假 TCP 封装。

最简单的,把 IP 协议号从 UDP(17) 改成 TCP(6),复杂点就换个传输头,都可取。比如换头:
GitHub - marywangran/pseudotcp-tunnel

此外,各大厂也有各类冠以自研修饰的 XX 协议,它们有多么复杂自然不必多说,主要目的是卷。

我给一个 10 行代码实现的假 TCP,找到 Linux 内核 tcp_data_queue 函数,从注释处增加代码:

static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)

        struct tcp_sock *tp = tcp_sk(sk);
        bool fragstolen;
        int eaten;

        if (sk_is_mptcp(sk))
                mptcp_incoming_options(sk, skb);

        if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq) 
                __kfree_skb(skb);
                return;
        

        skb_dst_drop(skb);
        __skb_pull(skb, tcp_hdr(skb)->doff * 4);

        // 增加下列 if 语句 13 行代码,暂硬编码 5001 端口,只为 iperf 测试。
        if (inet_sk(sk)->inet_num == 5001 && after(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) 
                struct sk_buff *nskb;
                u32 n = TCP_SKB_CB(skb)->seq;
                if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt + tcp_receive_window(tp)))
                        goto out_of_window;
                nskb = skb_copy_expand(skb, n - tp->rcv_nxt, 0, GFP_ATOMIC);
                if (likely(nskb)) 
                        skb_put_padto(nskb, n - tp->rcv_nxt + skb->len);
                        consume_skb(skb);
                        skb = nskb;
                        TCP_SKB_CB(skb)->seq = tp->rcv_nxt;
                
        

        tp->rx_opt.dsack = 0;

将这代码部署在接收端,事竟成。

意思是保证发送端 una 不断前推,不会收到 sack 和 dupack,也就不会重传。如下图:

简单说,收到空洞,空洞立即被填充,丢包补零不重传,这种粗暴策略意味着即使真发生乱序,数据也会被 0 填充掉。但简单啊。

看效果:

解释一下测试结果,为什么假 TCP 对丢包无感。

假 TCP 是有损传输,iperf 并不校验这种有损,所以即使结果带宽很大,有效的到达数据却随丢包率增加而减小。strace 观测 iperf 接收端实际接收到的数据:

这些 0 即被填充的 0,即有损传输的体现。丢包率越高,0 的比例越高。

结合优质高尚的编码可最大限度消除有损传输的质量伤害,还记得传皮鞋那个吗?参见:有损传输示例

再举一例。用有损传输方式接收一个 html 文档,会是一堆乱码吗?并不会。

若有数据丢失,至多丢失一些 HTML 标签和内容,HTML 标签封闭嵌套特点使标签自动补全变得可行且容易,最终依然可得到一个虽缺失部分内容但浏览器依然可展示的文档。

不仅如此,这些 0 还有妙用。通过分析一段时间内这些 0 的分布,绘制一幅拥塞画像是高尚的。
说说假 TCP 有什么用,到底哪里需要把 UDP 伪装成 TCP?

仅在接收端部署那 13 行代码,TCP 就五脏俱全了,这本就是一个真的 TCP,足以糊弄运营商的行为也就不能叫欺骗。除了不重传,TCP 的特性一样不少。

不重传意味着传输有损,收益是消除了重传时延,非常适合承载时延敏感的业务,如 VNC,流媒体。它适合传输任何 UDP 数据,以绕过运营商对 UDP 的一刀切管控,用这个协议搭建的隧道是通用,高尚的隧道。

两三事:

  • 常见的面试题,如何用 UDP 实现可靠传输,但不高尚。高尚的面试题应该是,如何用 TCP 实现不可靠传输。
  • TCP 的不可靠传输:TCP的不可靠传输
  • 搭建隧道时还在 TCP 和 UDP 中二选一吗?
  • 还在自研万行代码级的协议吗?本文只是个把戏,实际可以做得更好,比如给个窗口 Buffer,允许乱序的发生,Buffer full 了才补零。
  • 黑科技每周都有,你肯看,我就有。

周中有朋友问起假 TCP 的事,我说抽空写一篇。这种协议要看你想要多假,可以只封装个 TCP 头,但要更逼真,就要能让运营商设备识别 Session,这一步很重要,运营商正是因为容易识别 TCP Session,才视 TCP 为友好的。于是假 TCP 变得复杂… 反着做,在真 TCP 上做减法,把约束一个个卸掉,直到它足够假,也就假亦真时真亦假了。当然,三次握手要斟酌,对于长连接,这无关紧要,可对于短连接,重试成本低到是否可靠也同样无关紧要。不过我依然有办法连三次握手也优化掉,不过要下回分解。

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

以上是关于不可靠不重传的假 TCP的主要内容,如果未能解决你的问题,请参考以下文章

TCP 基本概念

TCP为什么是可靠的(怎么保证有效传输的)?

TCP 的超时与重传

如何在Wireshark中识别重传的TCP段?

TCP/IP详解 卷1 第二十一章 TCP的超时与重传

可靠的UDP连接 & MTU MSS