本页目录

WebRTC

浏览器之间直接传输音视频——WebRTC 用 SDP 协商媒体参数、ICE 打洞穿越 NAT、DTLS-SRTP 加密传输。P2P 的"不经过服务器"是性能优势,也是调试噩梦。

概述

WebRTC(2011)是浏览器内置的实时通信标准,不需要插件就能实现视频通话、屏幕共享和 P2P 数据传输。它组合了多个协议:SDP 做媒体协商、ICE 做 NAT 穿透、DTLS-SRTP 做媒体加密、SCTP 做数据通道。WebRTC 的连接建立是全异步的——candidate 逐个到达、逐个验证——Trickle ICE 大幅减少了连接时间。

架构栈

WebRTC 架构栈:从浏览器 API 到底层传输 Browser JS API(RTCPeerConnection, RTCDataChannel) WebRTC C++ stack(libwebrtc, Google) SDP 媒体协商 (offer/answer) ICE NAT 穿透 (STUN + TURN) DTLS 密钥交换 → 派生 SRTP 密钥 SRTP 加密的 RTP 流 SCTP 数据通道 UDP(首选) TCP(fallback) 五个协议共同构成"传输层"——SDP/ICE 负责建连协商,DTLS/SRTP 管媒体安全, SCTP 管数据通道;全部跑在 UDP 之上,打不通才退到 TCP。

信令:被故意留白的一环

WebRTC 不规定信令⁠——两端怎么交换 SDP 和 ICE candidate,标准完全不管,由你用 WebSocket/HTTP/任意通道自己实现。这是有意为之:媒体路径要 P2P,但"两个还没连上的人怎么先碰头"本就需要一个双方都能到达的第三方。

信令负责牵线,媒体/数据走 P2P 直连 信令服务器(自建,任意协议) 交换 SDP offer/answer + ICE candidate Peer A Peer B (协商完成后)SRTP 媒体 / SCTP 数据 —— P2P 直连 信令只负责"两端怎么找到对方",协议不限定(WebSocket/HTTP 皆可、标准不管); 一旦 offer/answer 和 candidate 交换完成,媒体和数据就完全绕开服务器直连。

先有信令把 offer/answer 递过去,ICE 才有 candidate 可连——这就是 WebRTC 的"先有鸡"。

SDP 协商

Caller → Callee (Offer):
  v=0
  o=- 1234567890 2 IN IP4 192.168.1.100
  s=-
  t=0 0
  a=group:BUNDLE audio video data     ← 同一传输链路上多路复用
  m=audio 49170 UDP/TLS/RTP/SAVPF 111 103
  c=IN IP4 192.168.1.100
  a=rtpmap:111 opus/48000/2
  a=rtcp-mux                           ← RTP 和 RTCP 同一个端口
  a=ice-ufrag:8sdf7g
  a=ice-pwd:asd88fg
  a=fingerprint:sha-256 AB:CD:...      ← DTLS certificate hash

Callee → Caller (Answer):
  m=audio 49180 UDP/TLS/RTP/SAVPF 111  ← 选定的端口 + codec
  ...

  → 双方协商完成 → ICE 开始连接检查

ICE

每个 peer 收集三种 candidate:

  1. host: 本地 IP:port
  2. srflx (server reflexive): STUN server 返回的公网 IP:port
  3. relay (relayed): TURN server 分配的中继地址

对每对 (local candidate + remote candidate) 做 connectivity check (STUN binding) → 选出最优可用 pair(优先级 host > srflx > relay,即"能直连就不走中继")。

STUN vs TURN 的分工⁠:STUN 只是"帮你问出自己的公网映射",不转发媒体,几乎零成本;但当两端都在对称 NAT 后、打洞失败时,就必须退到 TURN 中继⁠——所有媒体经它转发,带宽成本高,是部署里唯一绕不开的服务器成本。生产经验:约 10-20% 的连接最终落到 TURN。

Trickle ICE:不必等所有 candidate 收集完才发 offer。candidate 边发现边通过信令发给对端、边做连通性检查,把建连时间从"等最慢的 candidate"压缩到"第一个可用 pair 就绪"——这是 WebRTC 起连快的关键。

DTLS-SRTP

DTLS handshake (类 TLS 1.2 over UDP) → 握手完成后,双方从 DTLS 派生 SRTP 密钥 (不额外交换):

DTLS 握手:从 ClientHello 到派生 SRTP 密钥 ClientHello ServerHello Certificate ServerKey- Exchange ClientKey- Exchange ChangeCipher- Spec Finished DTLS-SRTP 扩展:协商使用哪种 SRTP 密码套件 双方从 DTLS master_secret 派生 SRTP 密钥 SRTP keys = SRTP_KDF(DTLS master_secret) SRTP 密钥不再单独传输——直接由本次 DTLS 握手产生的 master secret 派生, 天然与这次握手绑定,省掉一轮额外的密钥交换。

Data Channel

覆盖在 DTLS 上的 SCTP (Stream Control Transmission Protocol):

与 TCP 相比:
  - 多 stream (无 HOL blocking)
  - 保留消息边界
  - 可配置: 可靠 (reliable) / 部分可靠 (timed reliability)

适合: 文件传输, 游戏状态, 聊天

拥塞控制与 Simulcast

实时媒体不能像 TCP 那样靠丢包+重传——一帧的截止时间只有十几毫秒,等重传必然错过。WebRTC 用 GCC (Google Congestion Control):基于 RTP 包到达的延迟梯度预判拥塞(队列在涨 → 早降码率),而非等到真丢包,发送方据此动态调整编码码率。

Simulcast / SVC 解决多人会议的异构带宽:同一路摄像头同时编出多档⁠(如 180p/360p/720p),由中间服务器按每个接收端的网络分别下发合适的一档——这正是下面 SFU 能"按人选档"的前提。

SFU / MCU / Mesh — 多人拓扑

WebRTC 本是 P2P,但多人时 P2P 不可扩展,必须引入服务器。三种拓扑(与流媒体协议里的分发权衡同源):

多人拓扑三选一:Mesh / SFU / MCU Mesh N 人两两直连 → 每人上行 N-1 路 仅 3-4 人可用,无服务器 SFU 选择性转发:服务器只转发不解码,每人上行 1 路、按需下发(配合 simulcast) → 主流方案,可扩展,服务器 CPU 轻(不转码) MCU 混流:服务器解码所有流 + 合成一路再发 → 省接收端下行/算力,但服务器 CPU 极贵 SFU 只转发不解码,是可扩展性与服务器成本的平衡点,多人会议的主流方案; Mesh 仅适合超小规模,MCU 用服务器算力换客户端轻量但成本最高。

为何 WebRTC 难像 HLS 那样铺 CDN:每条连接有状态(ICE/DTLS/SRTP 上下文 + 实时拥塞反馈),不是可缓存的文件。规模化只能靠 SFU 级联,而非边缘缓存——这是"亚秒实时"换来的根本代价。

参考

  • webrtc.org: webrtc.googlesource.com
  • 工具⁠: chrome://webrtc-internals

Keywords: WebRTC, signaling, SDP, ICE, Trickle ICE, STUN, TURN, DTLS-SRTP, SCTP, Data Channel, GCC, simulcast, SVC, SFU, MCU, mesh, Opus