TCP对比UDP:加速场景的协议权衡

list 文章目录

TCP和UDP的对比通常被简化为一句话:TCP可靠,UDP不可靠。

这话在协议层面没错,但它把工程上最关键的一点藏起来了:可靠与不可靠不是好坏之分,而是取舍不同。TCP 选可靠,代价是排队和等待;UDP 不要可靠,换来速度和控制权。加速时也分成两条路:优化 TCP 主如果替它承担可靠性的延迟账单;优化 UDP 则是在’不保证送达‘的前提下有选择地补丢包。

两边的加速手段不能混用:把TCP那套逻辑搬到UDP上会伤实时性,把UDP那套搬到TCP上又解决不了窗口增长受限的问题。

TCP的代价:顺序交付与队头阻塞

TCP向应用层提供一个保证:数据按发送顺序到达,不丢、不重、不乱。为了维持这个保证,TCP在协议栈内部做了几件事。

接收端有一个重排缓冲区。IP 网络里乱序很常见,因为不同包可能走不同路线;只要顺序乱了,接收端就必须等缺的那个包回来,才能把后面的数据交给应用。这就是队头阻塞(Head-of-Line Blocking):丢一个包,后面所有已到达的包都被扣住。

链路越慢,它越要命。假设 RTT 是 200ms:丢一个包,接收端至少等 200ms 才能拿到重传;这期间后续到达的包全被压着。所以应用层看到的不是’200ms 后慢慢恢复‘,而是’先沉默 200ms,然后数据一起涌出来‘。网页上的表现是某个资源卡住后突然完成;实时应用则完全受不了——200ms 之前的游戏状态早就没用了。

TCP 的另一笔代价是拥塞控制太保守。慢启动时窗口增长很快,但逼近链路容量后,基于丢包的算法会主动丢包来试探上限,然后收紧窗口重新爬。短 RTT 链路上这很快;长 RTT 链路上,每丢一次包都要好几个 RTT 才能重回满速。

加速器如何介入TCP

TCP加速器的核心逻辑是:把TCP的代价隔离在短RTT段内。

加速器把一条 TCP 切成两段,每段的 RTT 都会大幅缩短,用户到节点这一段常常只有直连的 1/3 到 1/4。队头阻塞还是会存在,但它封锁的时间与 RTT 成正比,RTT 越短恢复越快。窗口恢复也一样:原本在 200ms RTT 上要 2 秒才能爬完的窗口,在 50ms RTT 上 0.5 秒就够了。

拆分也带来新问题:两段 TCP 各自独立,互相不知道对方的拥塞情况。用户到节点这段可能很宽,窗口一路涨;节点到目标这段却在排队,窗口被压住。数据于是高速灌进节点,堆在发送缓冲区里。堆得太多、超过’节点到目标‘这段的带宽延迟积时,额外的排队延迟就在加速器内部出现了。

这意味着,加速器的有效运作要求节点的缓冲区管理调度规则与两段链路的带宽资源差异匹配。如果入口链路远快于出海出口链路(比如用户是千兆宽带,出海出口链路起因跨洋而只有几十Mbps的有效吞吐量),节点必须主动向用户端施加反压——通过调整TCP窗口或ping 值ACK来降低入口段的发送速率。不做这个优化的加速器,会在节点内部制造自己的拥塞点。

不用研究节点表,也不用判断协议。狗急加速器把这些判断收进默认值,你只需要一次点击,网页加载和视频起播的时间随即缩短。

UDP的设计:不保证,不阻塞

UDP向应用层提供的是一个完全不同的契约:数据报尽力交付,不保证到达,不保证顺序。如果数据报丢失,UDP不丢过来重写。如果数据报乱序,UDP不重排。应用层收到什么就是什么,UDP不替应用做任何决定。

正是这种’让应用自己决定‘的设计,让很多实时应用选择了 UDP。游戏、VoIP、实时视频,要的是及时而不是完整:语音包丢了宁可听一小段静音,也不想等 200ms 后把过时的音频插进来;游戏里丢一次位置更新也没关系,只要更新够密,下一个包几毫秒内就把它覆盖了。

UDP把可靠性决策权交给了应用层。应用能自己实现选择性丢过来重写(只丢过来重写关键数据包),能实现FEC(用冗余换包丢失恢复),也能什么都不做(容忍包丢失)。这种灵活性是UDP在实时场景中的核心价值。

但UDP在公网上有一个结构性的劣势:网络中间设备对它的态度。

拥塞发生时,路由器的队列管理通常是偏向 TCP 的。像 WRED 这类主动队列管理算法会挑着丢 UDP 包,因为 UDP 不会像 TCP 那样看到拥塞信号就降速。于是不减速的 UDP 流在拥堵链路上被视为’不合作的流量‘,优先被标记或丢弃。这就是高峰期 UDP 丢包率通常高于 TCP 的原因——不是 UDP 更脆弱,而是设备策略在设计上偏向保护 TCP。

加速器对UDP能做什么

加速器的路由选择

UDP加速不要看拆分连接,起因UDP没有连接。UDP加速不要看管理拥塞窗口,起因UDP没有窗口。那么加速器在做什么?

路线选择仍然是第一位的。如果加速器能将UDP数据包从一条包丢失率2%的公共路线迁移到一条包丢失率0.2%的私有骨干网路线,快慢改善直接体现在应用层的包丢失减少上。这部分逻辑和TCP加速相同,但对UDP的收益往往更肉眼可见——起因UDP对包丢失没有自愈能力,丢一个就是一个。

在这条更优的路线之上,加速器能在两端节点之间增加FEC。发送端在连续N个数据包后附加M个冗余包,使得接收端在N+M个包中任意丢失不超过M个时都能完整恢复。FEC不消除包丢失——物理链路上的数据包仍然在丢失——但它让应用层感知到的包丢失率降到零。

FEC 的代价有两项:带宽开销和编码延迟。发送端要攒够一定数量的包才能生成冗余包:每秒 60 个更新包的游戏流量影响很小(攒 4 个包约 67ms),低频流量则可能等更久。这段积累时间加上编码运算的耗时,就是加速器额外引入的处理延迟。

真正的易用,是把复杂藏起来。狗急加速器把选节点、调协议、测线路压缩成一个开关,按下即连,首屏不再等待。

在工程实践中,经常观察到一个权衡:开启FEC后,游戏内显示的包丢失率慢下去,但基础ping值增加了3-5ms。对通常数玩家来说,包丢失率从1%降到0带来的体验改善远大于ping值增加5ms的代价。但对于对ping 值极度敏感的场景(比如职业电竞),这个权衡要看被明确意识到。

误判:QUIC与”UDP加速”

QUIC是一个基于UDP的传输协议,它在应用层实现了类似TCP的可靠传输和拥塞控制。很多加速服务声称支持”UDP加速”,但实际测试会发现它们对QUIC的处理并不一致。

一些加速器将QUIC流量当作普通UDP处理——单纯转发,不做FEC,不做路线优化。这是合理的,起因QUIC已经内置了拥塞控制和包丢失恢复,外部的FEC可能干扰QUIC自身的速率调节逻辑。但如果加速器对QUIC什么都不做,用户看到的效果就和直连没有区别(除了路线可能不同)。

另一些加速器会尝试对QUIC流量也做TCP式的分段中继——终结QUIC连接,在内部用自定义协议传输,到出海出口节点再重建QUIC连接。这种做法等于破坏了QUIC的一套到底加密和认证模型,通常要看客户端安装根证书才能中间人解密。对用户来说,带来的安全风险可能超过加速收益。

一个更容易混淆的情况是,加速器宣称”UDP加速”,但实际只优化了特定端口的UDP流量(比如常见的游戏端口),而对QUIC使用的443端口的UDP流量走的是另一套逻辑(甚至可能被降级为直连)。用户在测一下速度时看到UDP加速有效,但在使用QUIC的应用(比如基于HTTP/3的网站、某些视频流)时感受不到改善,原因就在这里——两种UDP流量经过了不同的处理管道。

bash
# 检查某个应用使用的UDP端口和协议
# QUIC通常使用UDP 443
ss -tunlp | grep 应用进程名

# 抓包观察UDP流量的行为模式
# QUIC的包通常较大,且有TLS握手的特征
tcpdump -i eth0 -n udp port 443 -c 50

协议的边界与选择

两者的差别,归根到底是对应用层的承诺不同。TCP 承诺可靠有序,所以加速器只能靠分段把这些承诺带来的延迟隔离开;UDP 什么都不承诺,加速器的活动空间看着更大,实际更受限制——不能假设包的语义、不能重排、不能随便加延迟,只能在路径优化和少量冗余里做文章。

这也决定了什么应用配什么手段。网页和 API 调 TCP,要压的是握手延迟和窗口爬升时间;游戏和实时通信用 UDP,要压的是丢包率和路径抖动。把为 TCP 设计的方案套到 UDP 上(比如强制分段中继),要么破坏实时性,要么因为协议不匹配连不上。

反过来,如果试图用UDP的”尽力而为”逻辑去加速TCP——比如只做路线转发不做连接分段——那等于放弃了所有TCP层面的优化可能,退化为一个纯粹的VPN。

理解两个协议的设计取舍,不是为了背特性清单,而是看到某个加速方案时,能判断它的核心逻辑围着哪个协议转,跟你的流量有多少匹配度。两者不能互换,而多数用户并不知道自己在跑哪个协议。先查清流量的协议构成,比直接对比延迟数字更有用。

作者

狗急加速器技术团队

跑满带宽才是真本事

不用研究节点表,也不用判断协议。狗急加速器把这些判断收进默认值,你只需要一次点击,网页加载和视频起播的时间随即缩短。

协议再合理,如果带宽被卡着,实际体验也上不去。看视频、下大文件、开高清会议,考验的是持续吞吐而不是瞬时峰值。

狗急加速器在流量上不做限制,专线本身的承载能力也按高负载场景配置,长时间跑大流量不会出现速度逐步下滑的情况。

看视频会议该关注哪个数字

远程会议卡顿的时候,多数人第一反应是网速不行。其实会议对带宽的要求并不高,真正让它卡住的是抖动和丢包——画面压缩、声音断续都来自这里。

所以这时该做的是减少对链路的争抢:关掉正在下载的程序,别让家里其他人同时进行大流量操作。

如果这些做完还卡,那才是线路本身的问题。不限量的专线在这一点上优势明显:带宽有余地,临时的大量突发也能吸收。