WebRTC 核心概念详解
本文系统梳理 WebRTC 的五大核心概念:信令、SDP、ICE/STUN/TURN、DTLS+SRTP、GCC 拥塞控制,并深入讲解 NAT 穿透原理和 GCC 算法细节。
缩写全称速查
| 缩写 | 全称 | 中文 |
|---|---|---|
| SDP | Session Description Protocol | 会话描述协议 |
| ICE | Interactive Connectivity Establishment | 交互式连接建立 |
| STUN | Session Traversal Utilities for NAT | NAT 会话穿越工具 |
| TURN | Traversal Using Relays around NAT | 通过中继穿越 NAT |
| DTLS | Datagram Transport Layer Security | 数据报传输层安全协议 |
| SRTP | Secure Real-time Transport Protocol | 安全实时传输协议 |
| RTP | Real-time Transport Protocol | 实时传输协议 |
| RTCP | RTP Control Protocol | RTP 控制协议 |
| GCC | Google Congestion Control | 谷歌拥塞控制算法 |
| NAT | Network Address Translation | 网络地址转换 |
| TLS | Transport Layer Security | 传输层安全协议 |
| UDP | User Datagram Protocol | 用户数据报协议 |
| TCP | Transmission Control Protocol | 传输控制协议 |
| NACK | Negative Acknowledgement | 否定确认(丢包重传请求) |
| FEC | Forward Error Correction | 前向纠错 |
| REMB | Receiver Estimated Maximum Bitrate | 接收端估计最大码率 |
| BWE | Bandwidth Estimation | 带宽估计 |
一、信令(Signaling)
一句话:WebRTC 两端想通话,但双方都不知道对方在哪、能用什么格式——信令就是用来交换这些「元信息」的通道。
比喻:你要和朋友打电话,但没有存他号码。你们先互发微信消息「我的号码是 xxx,你的是多少?」——这个过程就是信令。
关键点:
- WebRTC 不规定信令用什么协议,随便你用 WebSocket、HTTP、甚至短信
- 信令只传两类内容:SDP(我能做什么)和 ICE Candidate(我在哪)
- 信令服务器本身不碰媒体流,只是个「中间人」转发消息
1 | A ──── Offer SDP ────→ 信令服务器 ──→ B |
二、SDP(Session Description Protocol)
一句话:我把自己的「能力清单」写给你看——我支持哪些编解码器、用什么分辨率、监听哪个端口。
比喻:相亲前双方交换简历,写清楚「我身高 180、会做饭、有房」,对方看完说「OK 我接受,我的情况是 xxx」。
真实的 SDP 长这样(截取片段):
1 | v=0 |
Offer / Answer 模型:
1 | A 发 Offer:「我支持 VP8 和 H264,你选一个」 |
三、ICE / STUN / TURN
核心问题:互联网上大多数设备在 NAT 后面(路由器后),没有公网 IP,两端怎么直连?
3.1 STUN 和 TURN 的本质区别
一句话区别:
- STUN:照镜子,告诉你自己的公网地址是什么,不转发流量
- TURN:快递柜,P2P 打不通时帮你中转所有流量
| STUN | TURN | |
|---|---|---|
| 作用 | 告诉你「你家门牌号是 xxx」 | 帮你把信收下,再转交给朋友 |
| 流量过服务器吗 | ❌ 不过,只是查询 | ✅ 所有流量都过 |
| 费用 | 极低(只有查询请求) | 高(要承载所有音视频流量) |
| 使用时机 | 每次连接都用 | 仅 P2P 打不通时兜底 |
3.2 STUN——「你的公网地址是什么」
比喻:你站在镜子前不知道自己长啥样,STUN 服务器就是那面镜子,照完告诉你「你的公网 IP 是 1.2.3.4:5678」。
1 | 你(192.168.1.100:12345) |
3.3 ICE——「尝试所有路径,找最优的一条」
ICE 会枚举三类 Candidate(候选地址),按优先级依次尝试:
| 类型 | 是什么 | 优先级 |
|---|---|---|
| host | 本机局域网 IP | 最高(同局域网直连) |
| srflx | 通过 STUN 拿到的公网 IP | 中(NAT 穿透) |
| relay | TURN 服务器中继地址 | 最低(兜底) |
1 | ① 先试 host candidate(局域网直连)→ 最快 |
3.4 TURN——「P2P 打不通时的中继」
走 TURN 就是走公网服务器代理所有媒体流:
1 | P2P 直连(host/srflx): |
走 TURN 的影响:
- 延迟增加:多绕一跳,增加 20-100ms(取决于 TURN 服务器离你多远)
- 带宽成本高:服务器要承载完整双向媒体流,1路 720p 约 1-2Mbps
- 内容仍然安全:媒体流是 SRTP 加密的,TURN 服务器只转发密文,看不到内容
生产环境策略:
- 能 P2P 直连的绝不走 TURN(省成本)
- TURN 服务器尽量部署在离用户近的地区(降延迟)
- 监控 TURN 使用率,超过 30% 说明 P2P 打洞成功率偏低,需排查
它们不是路由器实现,代码里只需配置地址:
1 | val iceServers = listOf( |
四、NAT 深挖
4.1 NAT 是什么,为什么存在
IPv4 地址只有 43 亿个,全球设备远不止这个数。NAT 的本质是「地址复用」——一个公网 IP 后面可以藏几百台设备,路由器负责做地址映射。
1 | 你的手机(192.168.1.100:12345) |
4.2 NAT 的四种类型(从容易到难穿透排序)
① 完全锥型 NAT(Full Cone NAT)
1 | 规则:只要内网 A 发过包,任何人都能从 1.2.3.4:54321 发包进来找到 A |
② 地址限制锥型 NAT(Address Restricted Cone)
1 | 规则:只有 A 主动发过包的目标 IP,才能回包进来(端口不限) |
③ 端口限制锥型 NAT(Port Restricted Cone)
1 | 规则:只有 A 主动发过包的目标 IP + Port,才能回包进来 |
④ 对称型 NAT(Symmetric NAT)
1 | 规则:每次发往不同目标,路由器分配不同的公网端口 |
对称型 NAT 是 P2P 打洞最大的敌人——你通过 STUN 拿到的公网端口,只对 STUN 服务器有效,发给对端时路由器又换了个新端口,对端根本找不到你。
4.3 P2P 打洞原理(以端口限制锥型为例)
两端同时向对方「假装」发包,让路由器在 NAT 表里留下记录,之后对方的包就能进来了。
1 | A(192.168.1.100) NAT-A(1.2.3.4) NAT-B(5.6.7.8) B(192.168.1.200) |
关键在于「同时」——两端必须几乎同时发包,这就是为什么信令服务器要协调双方同步开始。
4.4 对称型 NAT 为什么打不通
1 | A 通过 STUN 拿到公网地址 1.2.3.4:5678(STUN 服务器看到的端口) |
此时只能走 TURN 中继。现实中企业内网、运营商级 NAT(CGNAT)大量使用对称型 NAT,P2P 打洞成功率约 70-80%,剩下 20-30% 必须靠 TURN 兜底,所以生产环境不能省 TURN。
五、DTLS + SRTP
一句话:WebRTC 的所有媒体流强制加密,不能关,不存在明文传输。
为什么需要两层?
| 层 | 协议 | 作用 |
|---|---|---|
| 密钥交换 | DTLS | 基于 UDP 的 TLS,握手阶段交换加密密钥 |
| 媒体加密 | SRTP | 用 DTLS 协商出来的密钥,加密每一帧音视频 |
比喻:
- DTLS = 见面时互换暗语(密钥)
- SRTP = 之后说话都用暗语加密
流程:
1 | 1. ICE 建立 UDP 连接 |
为什么用 DTLS 不用 TLS?
因为 RTP 跑在 UDP 上,UDP 没有 TCP 的可靠传输,TLS 依赖 TCP,所以需要能在 UDP 上工作的 DTLS。
六、GCC 拥塞控制深挖
GCC 本质是两个并行的带宽估计器,结果取最小值:
1 | ┌── 基于延迟的估计器(Trendline Filter)──┐ |
6.1 估计器一:基于延迟梯度(Trendline Filter)
核心思想:网络队列开始积压时,包的到达间隔会变长,比丢包更早暴露拥塞。
第一步:计算每个包组的延迟梯度
WebRTC 把包分组(每组若干个包),测量「发送间隔」和「到达间隔」的差值:
1 | 发送端:包组1在 t=0 发出,包组2在 t=20ms 发出 → 发送间隔 20ms |
δ > 0 说明队列在积压(拥塞信号);δ < 0 说明队列在消散(网络变好)。
第二步:Trendline 滤波,过滤抖动噪声
单个包的 δ 抖动很大(网络天然不稳定),用线性回归拟合最近 N 个 δ 的趋势线:
1 | δ序列:+2, -1, +3, +4, +5, +6 → 趋势向上 → 拥塞信号 |
第三步:状态机判断
1 | NORMAL ──(trend持续上升)──→ OVERUSE(拥塞) |
只有连续多个包组都显示 OVERUSE,才触发降码率(避免误判)。
第四步:AIMD 调整码率
1 | UNDERUSE → 加性增加(Additive Increase):每秒增加 8% 码率 |
为什么「加慢减快」:降码率是应急响应,必须快;加码率如果太猛会重新触发拥塞,所以慢慢探测上限在哪。
6.2 估计器二:基于丢包率(Loss-based)
接收端通过 RTCP 报告丢包情况(每秒一次),发送端根据丢包率调整:
1 | 丢包率 < 2% → 可以提升码率(乘以 1.08) |
6.3 两个估计器为什么都需要
| 延迟梯度 | 丢包率 | |
|---|---|---|
| 响应速度 | 快(队列刚积压就感知到) | 慢(必须等到包真的丢了) |
| 准确性 | 受网络抖动影响,有误判 | 准确,丢了就是丢了 |
| 适用场景 | 提前预防拥塞 | 确认拥塞已发生 |
取两者最小值 = 保守策略,哪个说要降就降,不冒险。
6.4 完整 GCC 工作闭环
1 | 发送端 接收端 |
现代 WebRTC 主要用 Transport-CC(接收端把每个包的到达时间戳都反馈给发送端,发送端自己算延迟梯度),比老的 REMB 更精准。
七、串起来看整个流程
1 | 1. A 和 B 各自通过 STUN 拿到自己的公网地址 |
最后更新:2026-06