深入解析:WEBRTC

WebRTC 详解:无需服务器中转的浏览器间实时通信

打开一个视频通话应用时,音视频乃至聊天消息,往往并不经过服务器中转,而是直接在浏览器之间传输。实现这一切的就是 WebRTC(Web Real-Time Communication)。它把信令、NAT 穿透、加密、数据传输等多项技术捆绑在一起,把 P2P 通信带到了浏览器这个普及程度最高的平台之上。本文详细拆解这套机制的构造与取舍。

WebRTC 是什么

WebRTC 是一整套 API 与协议的统称,让浏览器与移动应用能够不经过服务器中转、直接交换音频、视频以及任意数据。Google 于 2011 年将其作为开源项目发布,此后标准化工作在两条战线同时推进:W3C 负责面向浏览器的 JavaScript API,IETF 负责底层协议,最终于 2021 年分别落地为 W3C 的 WebRTC 1.0 推荐标准,以及以 RFC 8825(概述)为代表的一系列 IETF RFC。在 WebRTC 之前,浏览器中的实时音视频通信要依赖 Flash、原生应用 SDK 或专用插件;WebRTC 让「打开一个浏览器标签页」就足以进行 P2P 实时通信。

WebRTC 的核心价值在于,把浏览器这一限制颇多的环境中原本需要各自实现的诸多环节(信令、NAT 穿透、加密、拥塞控制、媒体编解码)统一打包成一套标准 API。开发者只需操作 RTCPeerConnection 这一个对象,就无需直接面对其背后运行的 ICE、STUN/TURN、DTLS、SRTP、SCTP 等复杂协议栈的细节。

核心组成

RTCPeerConnection
WebRTC 的核心 API,代表与单个对端的一条连接。添加媒体轨道与数据通道、收集 ICE 候选、生成与应用 SDP offer/answer 等建立连接所需的几乎所有操作,都通过这一对象完成。
MediaStream(getUserMedia)
通过 navigator.mediaDevices.getUserMedia() 从摄像头、麦克风等设备获取的音视频流。将获取到的轨道添加进 RTCPeerConnection,即可直接发送给对端。用于屏幕共享的 getDisplayMedia() 属于同一族 API。
RTCDataChannel
用于传输媒体以外任意数据的双向通道。底层实现是 SCTP(Stream Control Transmission Protocol)跑在 DTLS 之上(SCTP over DTLS)。既支持类似 TCP 的可靠、有序模式,也可以通过 orderedmaxRetransmits 等选项选择更接近 UDP 的低延迟、不保证顺序的模式。
ICE(Interactive Connectivity Establishment)
用于在两个对端之间找出实际可通信路径的框架(RFC 8445)。双方各自收集若干候选路径并互相交换,再逐一进行连通性检测,最终选出可用且最优的一对候选路径。
SDP(Session Description Protocol)
用文本格式(RFC 8866)描述即将建立的媒体会话:所用编解码器、媒体类型、加密参数、ICE 候选等。WebRTC 通过一轮「offer/answer」的 SDP 交换来就会话内容达成一致。

连接建立的过程

  1. 信令(signaling):两个对端交换 SDP offer/answer 与 ICE 候选的阶段。需要特别指出的是,WebRTC 规范本身并未规定信令的传输方式,只要 SDP 能够送达即可。因此实际实现中通常借助 WebSocket 服务器或其他消息服务来完成,也就是说,即便媒体与数据本身是 P2P 传输的,建立连接这一「牵线搭桥」的过程仍然离不开某种服务器(下一节将详细介绍信令的具体方式)。
  2. 收集 ICE 候选:各对端收集自己可能被对方触达的候选路径,大致分为三类:代表本地网络接口地址的 host 候选、通过向 STUN 服务器查询而得知的公网可见地址即 server reflexive 候选,以及当直接连通无法建立时用作中继点的 relay 候选(经由 TURN 服务器)。
  3. 通过 STUN/TURN 实现 NAT 穿透:STUN 服务器所做的仅仅是告诉客户端「你在外部看起来是什么样子」,配合UDP 打洞技术,在多数 NAT 环境下已足以打通一条直连路径。当对称型 NAT 等情况导致直连始终无法建立时,TURN 服务器就作为「最后手段」在双方之间转发数据包。
  4. DTLS 握手:确定实际可用路径后,双方在该路径上进行 DTLS(Datagram TLS)握手,完成密钥交换与双向身份认证。
  5. 媒体/数据的收发:在已建立的加密路径之上,音视频以 SRTP(Secure RTP)形式传输,任意数据以 SCTP 形式传输。此后的全部实际数据都在这条加密隧道中流动。

这里有一点值得强调:WebRTC 是 P2P,但并非「无服务器」。信令环节必须依赖某种服务器(或至少是一条既有的通信通道),而绝大多数实际部署也都会准备 STUN 服务器,并额外部署 TURN 服务器作为直连失败时的兜底。对称型 NAT 叠加严格防火墙等直连根本无法建立的情况在实践中并不罕见,此时经由 TURN 中继的通信,实质上与「经服务器转发」并无区别。WebRTC 提供的是「优先尝试直连、失败则回退到中继」的设计,而不是一套彻底消除服务器的机制。

信令详解

WebRTC 的协商基于 JSEP(JavaScript Session Establishment Protocol,RFC 8829)模型。规范唯一要求的,只是把 SDP offer/answer 与 ICE 候选送达对端,至于用什么方式传输,是 WebSocket、HTTP、电子邮件,甚至手动复制粘贴,规范都刻意留白。这种自由度带来了实现上的多样性,但「何时、以何种方式交换 ICE 候选」这一选择,会显著影响连接建立的速度与实现复杂度。

Non-Trickle ICE(Vanilla ICE)

最朴素的方式:调用 setLocalDescription() 后,先等待 ICE 候选收集完全结束,再一次性交换包含全部候选的完整 SDP。优点在于信令收敛为一次往返,一份 offer、一份 answer,因此即便是不具备实时性的通道(单次 HTTP 请求、二维码、手动复制粘贴)也能完成整个交换。缺点是候选收集过程包含枚举网络接口、STUN 查询、TURN 分配等步骤,一旦某个服务器无响应就可能因等待超时而耗费数秒,期间用户只能干等,因此不适合对连接建立体感速度敏感的场景。

Trickle ICE

Trickle ICE(RFC 8838)不等候选收集结束,而是先立即发送不含(或仅含少量)候选的 SDP,此后每发现一个新候选,就通过 onicecandidate 事件像「涓流(trickle)」一样逐个发送给对端;接收端则用 addIceCandidate() 逐个添加,候选一到即可立即开始 NAT 穿透 所需的连通性检查。这样一来,SDP 交换、候选收集、连通性检查三个环节得以并行推进,大幅缩短了连接建立所需的时间,浏览器的 WebRTC 实现也因此默认采用 Trickle ICE。其前提是需要一条能随时传递候选的双向低延迟信令通道(常见做法是使用 WebSocket)。两端都需要支持 trickle,不过兼容性可以平滑退化:对不支持 trickle 的对端,仍可按 Non-Trickle 的方式处理。

WHIP / WHEP:基于 HTTP 的标准化信令

信令自由的另一面是碎片化:各家服务自造协议,导致推流设备与软件之间几乎没有互操作性可言。IETF 对此给出的回应是 WHIP(WebRTC-HTTP Ingestion Protocol,RFC 9725):用一次 HTTP 往返完成 SDP 交换。它面向的是推流(ingest)场景:编码器(如 OBS)向媒体服务器「推送」媒体时,用 HTTP POST 发送 SDP offer,在响应中收到 answer 即完成协商。面向观众拉流(egress)的配套规范是 WHEP(WebRTC-HTTP Egress Protocol),目前仍在标准化推进之中。其动机是用具备亚秒级延迟的 WebRTC 取代长期由 RTMP 承担的直播推流角色。基本流程本质上就是 Non-Trickle 的一次往返,但也以可选项形式规定了通过 HTTP PATCH 实现 Trickle ICE 与 ICE restart。随着 OBS Studio 以及主流 CDN、媒体服务器陆续跟进支持,WHIP 正在确立其「WebRTC 时代的 RTMP」的地位。

维度Non-Trickle(Vanilla)Trickle ICEWHIP/WHEP
SDP 交换形态/次数一次往返,SDP 已包含全部候选多轮:先发不含候选的 SDP,候选随后逐个补发一次 HTTP 往返(可选 PATCH 追加 trickle)
候选发送方式收集完毕后随 SDP 一次性发送通过 onicecandidate 逐个「涓流」式发送默认随初始 SDP 一次性发送,可选 HTTP PATCH 追加
连接建立速度较慢,需等待候选收集全部结束较快,交换与收集并行推进与 Non-Trickle 相当,追加 trickle 后可提升
对信令通道的要求只需能传达一次消息(HTTP、二维码、复制粘贴均可)需要双向、低延迟的实时通道(如 WebSocket仅需支持 HTTP POST/PATCH,无需长连接
典型用途无实时信令通道的简单场景浏览器间标准 WebRTC 通话/会议OBS 等编码器向媒体服务器推流、CDN 拉流

网状信令:迈向无服务器信令

P2P 网络一旦长成规模,已经建立好的 P2P 连接本身,就足以为新连接的加入承载信令:已经入网的节点充当中间人,经由自己与新加入者之间既有的数据通道,转发对方与网络其他成员交换 SDP/ICE 所需的消息。随着越来越多的信令流量改走这条路径,专用信令服务器承担的负载随之下降,网络对信令服务器故障的韧性也相应提高。

但鸡与蛋的约束依然存在:一个尚无任何连接的全新参与者,其「第一条连接」终究还是要依赖某种外部引导,比如服务器、中继,或是某条已经存在的通信通道。网状信令只有在网络已经存在之后才谈得上用武之地,它能省去的是「后续每一个新节点」的信令成本,而不是「从零开始」这一步本身。

更进一步的变体,是用 Nostr 这类通用的 pub/sub 中继取代专用信令服务器,作为传递信令消息的载体。这种做法下有一套惯用的隐私设计:签署信令消息所用的,是与应用层持久身份完全解耦的一次性临时密钥,真正的身份信息只存在于已加密的载荷内部,这样一来,就连中继的运营者自己,也无从得知究竟是谁在与谁建立连接。

安全性

WebRTC 中,媒体与数据通道都强制加密,规范本身根本不提供「不加密」这个选项。DTLS 负责协商会话密钥,此后音视频始终以 SRTP 形式、数据通道始终以 DTLS 承载的 SCTP 形式收发,未加密的传输方式不存在。

  • IP 地址泄露的隐患:在 ICE 候选收集过程中,浏览器会生成暴露本地私网 IP 地址的候选,以及通过 STUN 查询而暴露公网 IP 地址的候选。即便用户正在使用 VPN,这套候选生成机制也可能把真实 IP 地址暴露出去,这就是曾广受关注的「WebRTC IP 泄露」问题。
  • 通过 mDNS 候选来缓解:目前主流浏览器已默认启用相应缓解措施:不再直接公开本地 host 候选的原始 IP 地址,而是用一个随机生成的 .local mDNS 主机名替代后再共享。这样既能保证同一局域网内的连接正常建立,又能避免向无关第三方泄露真实的私网 IP 地址。

拓扑结构的局限:从全网状到 SFU/MCU

对于一对一通话,两个对端直接建立连接的全网状(full mesh)结构完全没有问题。但当参会人数达到 n 人、所有人都试图维持全网状连接时,每个对端都要同时维护 n-1 条连接,并持续向其余所有人上传自己的音视频。连接数会以 O(n²) 的速度增长,每个客户端的上行带宽与处理能力很快就会成为瓶颈,这套方案只适用于人数较少的通话场景。

面对这一局限,现实中的解法是重新引入中心化的服务器角色,即 SFU(Selective Forwarding Unit,选择性转发单元)MCU(Multipoint Control Unit,多点控制单元)。SFU 本质上是一台路由器:它接收每个参会者的媒体流,不做解码直接转发给其他参会者,这样每个客户端只需承担一路上传,加上相当于 (n-1) 路的下载。MCU 则更进一步,在服务器端把所有人的音视频混合、合成为单一流后再下发,从而降低客户端的负担,但也大幅提高了服务器端的计算成本。主流视频会议服务大多采用 SFU 方案。这本质上是「P2P 理想」与「面向多人的现实可扩展性」之间的取舍,与 P2P 与元宇宙 中讨论的多人同时连接问题一脉相承。

应用场景

  • 视频会议与语音通话:Google Meet、Discord、Zoom 浏览器版等诸多视频会议服务,都以 WebRTC 作为媒体传输的基础。
  • 文件传输:借助 RTCDataChannel,浏览器之间可以直接互传文件,无需先上传到服务器再转发。
  • 浏览器中的 P2P 网络:RTCDataChannel 并非只服务于媒体传输,它同样是一条可承载任意二进制数据的通用 P2P 管道。基于这条管道,WebTorrent 让浏览器能够直接加入类似 BitTorrent 的分享群组(swarm);浏览器版 IPFS 节点以及 libp2p 的 WebRTC 传输层,也让浏览器无需安装专用应用,就能成为 P2P 网络中名副其实的参与者。这意味着此前只能从服务器下载的浏览器,如今也能作为对等节点承担上传的角色,这一转变颇具意义。

对比:WebSocket、WebTransport、WebRTC DataChannel

维度WebSocketWebTransportWebRTC DataChannel
连接形态客户端-服务器(始终为一对一,必须有服务器)客户端-服务器(始终为一对一,必须有服务器)端到端(服务器仅协助信令与 NAT 穿透)
传输层TCPQUIC(基于 UDP)SCTP over DTLS(基于 UDP)
可靠性与顺序性固定为 TCP 语义:始终可靠、始终保序按流可选;数据报(datagram)不保证可靠可按流通过 orderedmaxRetransmits 等选项灵活选择
是否需要信令不需要:直接连接 URL 即可不需要:直接连接 URL 即可需要:必须预先交换 SDP/ICE 候选
主要用途通用的客户端-服务器实时双向通信(聊天、通知等)低延迟的服务器通信、媒体流、多路数据报传输视频会议的音视频/聊天、P2P 文件传输、浏览器 P2P 网络

相关页面

WebRTC 得以尝试直连的根基,正是 NAT 穿透 中介绍的 STUN/TURN/ICE 机制;信令环节则通常借助 WebSocket 实现。多人连接时暴露出的可扩展性问题,与 P2P 与元宇宙 中讨论的并发连接数问题一脉相承;SFU/MCU 这类拓扑取舍在一对多的直播场景中演变为分发树与级联,详见 P2P 直播。而基于 RTCDataChannel 构建的浏览器 P2P 网络(如 WebTorrent),则直接继承了 BitTorrent 的分享群组机制。如果只是需要与服务器进行简单的双向通信,WebRTC 的复杂度其实并无必要,这种场景可参考更轻量的 WebSocketWebTransport

返回首页