2、网络基础与延迟模型:RTT、RTP/RTCP协议基础、排队延迟与自延迟模型、TCP与UDP的拥塞控制差异
各位同学,欢迎来到第二章。上一章我们聊了WebRTC的整体架构,今天要扎进网络底层了。说实话,拥塞控制算法再花哨,如果不懂网络延迟的构成,那基本就是盲人摸象。我个人习惯,在分析任何拥塞控制问题之前,先把网络模型画清楚。
这一章,我们重点解决几个核心问题:RTT到底怎么算?RTP和RTCP在拥塞控制里扮演什么角色?排队延迟和自延迟模型是什么鬼?以及,为什么TCP那套拥塞控制不能直接搬到WebRTC里用?
2.1 RTT:往返时间的真相
RTT,全称Round-Trip Time,往返时间。说白了,就是数据包从A到B,再回到A的总耗时。在WebRTC里,RTT是拥塞控制最核心的输入参数之一。你想想看,如果不知道网络延迟有多大,你怎么判断该不该多发数据?
RTT的构成其实不复杂,但很多人容易忽略细节。它主要由三部分组成:
- 传输延迟:物理链路上的传播时间。光速在光纤里大约2/3c,这个基本固定。
- 处理延迟:路由器、交换机处理数据包的时间。通常很小,几微秒到几十微秒。
- 排队延迟:数据包在路由器缓冲区里等待的时间。这个最要命,也是拥塞控制要重点对付的。
关键点:在WebRTC的拥塞控制中,我们真正关心的是排队延迟的变化量,而不是RTT的绝对值。因为传输延迟和处理延迟基本不变,RTT的波动几乎完全来自排队延迟。
我记得有一次排查线上问题,用户反馈视频卡顿,但RTT看起来只有30ms。后来仔细一查,发现RTT在30ms到120ms之间剧烈抖动。嗯,这就是典型的排队延迟在作怪。如果只看平均RTT,你根本发现不了问题。
2.2 RTP/RTCP协议基础
RTP(Real-time Transport Protocol)和RTCP(RTP Control Protocol)是WebRTC的左右手。RTP负责运媒体数据,RTCP负责反馈网络状态。两者缺一不可。
2.2.1 RTP:媒体数据的搬运工
RTP包的结构其实挺简洁的。每个RTP包都有一个序列号(sequence number)和时间戳(timestamp)。这两个字段在拥塞控制里至关重要。
- 序列号:用于检测丢包和乱序。接收端通过序列号可以知道哪些包丢了,哪些包到了。
- 时间戳:用于计算延迟和抖动。发送端打上时间戳,接收端收到后对比本地时间,就能算出单向延迟。
个人经验:我建议你在实现拥塞控制时,一定要仔细处理RTP序列号的回绕问题。序列号是16位的,最大65535。我曾经见过一个团队因为没处理好回绕,导致丢包率计算直接崩了,视频画面花成一团。
2.2.2 RTCP:网络状态的晴雨表
RTCP是RTP的控制协议。它定期发送报告,告诉发送端网络状况。常用的报告有两种:
- SR(Sender Report):发送端报告,包含发送端的NTP时间戳和RTP时间戳。
- RR(Receiver Report):接收端报告,包含丢包率、抖动、最后收到的序列号等信息。
WebRTC的拥塞控制算法,比如GCC(Google Congestion Control),就是基于RTCP RR中的信息来调整发送速率的。说白了,接收端告诉发送端:「兄弟,网络有点堵,你慢点发。」
注意:RTCP的发送频率不能太高,否则会占用带宽。WebRTC默认RTCP间隔是5秒,但在拥塞控制中,这个间隔太长了。实际实现中,GCC会动态调整RTCP的发送频率,在需要时缩短到几百毫秒。
2.3 排队延迟与自延迟模型
排队延迟,是拥塞控制的核心战场。为什么这么说?因为网络拥塞的本质,就是数据包在路由器缓冲区里排队排太久了。
自延迟模型(Self-induced Delay Model)是理解排队延迟的关键。它的核心思想很简单:
如果你发送数据的速度超过了瓶颈链路的带宽,数据包就会在路由器缓冲区里堆积,导致延迟增加。
反过来,如果你发送速度低于瓶颈带宽,缓冲区就会清空,延迟下降。所以,通过观察延迟的变化,你就能推断出网络是否拥塞。
核心公式:
排队延迟 = 当前RTT - 最小RTT(base delay)
其中,最小RTT是在网络空闲时测得的RTT,代表传输延迟+处理延迟。当前RTT减去最小RTT,就是排队延迟。
我刚开始做WebRTC时,对自延迟模型的理解不够深。有一次,我直接拿RTT的绝对值来做拥塞判断,结果算法在WiFi环境下完全失效。后来才明白,WiFi的传输延迟本身就不稳定,必须用排队延迟(RTT减去base delay)才能准确反映拥塞状态。
2.4 TCP与UDP的拥塞控制差异
这个问题,面试的时候经常被问到。很多人会说「TCP可靠,UDP不可靠」,但拥塞控制层面的差异远不止这些。
| 维度 | TCP | UDP(WebRTC) |
|---|---|---|
| 拥塞信号 | 丢包为主(早期),延迟为辅(BBR等) | 延迟为主(GCC),丢包为辅 |
| 控制方式 | 窗口控制(cwnd) | 速率控制(bps) |
| 公平性 | TCP友好(AIMD) | 需要与TCP共存(TFMCC) |
| 实时性 | 低(重传导致延迟大) | 高(允许丢包,不重传) |
| 反馈机制 | ACK确认 | RTCP报告 |
为什么TCP的拥塞控制不能直接用在WebRTC里?原因很简单:TCP是面向可靠性的,而WebRTC是面向实时性的。
TCP看到丢包,第一反应是「赶紧重传」,然后降低发送窗口。但WebRTC看到丢包,第一反应是「丢就丢了,继续发新的」。因为视频帧过期了,重传也没意义。
另外,TCP的AIMD(加性增乘性减)策略在实时通信中太激进。TCP丢包后窗口减半,但视频编码器没法瞬间把码率减半。你想想看,如果视频码率从2Mbps突然降到1Mbps,画面质量会断崖式下降,用户直接骂娘。
避坑指南:我曾经在一个项目中尝试把TCP的CUBIC算法移植到WebRTC里,结果惨不忍睹。视频在弱网环境下频繁卡顿,因为CUBIC对丢包太敏感了。后来老老实实用GCC,基于延迟的拥塞控制才是实时通信的正解。
总结一下这一章的核心:RTT是基础,但我们要关注的是排队延迟;RTP/RTCP是工具,提供了拥塞控制所需的反馈信息;自延迟模型是理论,解释了延迟与拥塞的关系;TCP与UDP的差异是实践,告诉我们为什么不能照搬。
嗯,这一章的内容就到这里。下一章我们会深入GCC算法的具体实现,看看Google是怎么把延迟模型变成真正的拥塞控制代码的。