TT Lab
开始
学习 学习路径 课程

实时通信 — WebSocket、gRPC 流式调用与 WebRTC

WebRTC — 信令要自己做

在 TT Lab 中继续学习

一句话总结

WebRTC 没有规定如何建立连接,所以要自己构建用于传递 SDP 的信令,用 ICE 寻找路径,用 DTLS 交换密钥,然后用 SRTP 传输媒体,用 SCTP 传输数据通道。

为什么需要它

要让两台设备直接对话,有三个问题需要解决:如何互相告知对方的地址和能力(信令);两者都在 NAT 之后时,通过哪条路径到达(ICE、STUN、TURN);如何保护这条路径上的数据(DTLS-SRTP)。RFC 8825 所概述的 WebRTC,规定了后两者,却有意把第一个留空。JSEP(RFC 9429) 只规定浏览器如何生成 SDP 提议与应答,至于如何把它传给对方,则交给应用程序。所以几乎所有服务都会另设一个像 WebSocket 这样的小型信令服务器。

工作原理

SDP 提议与应答。提议方先确定要发送什么(音频轨道、数据通道),再用 createOffer 生成 SDP。如果没有确定要发送的内容,就会得到一个没有 m= 行的空提议。SDP 中包含每种媒体的 m= 行、编解码器(a=rtpmap)、ICE 凭据(a=ice-ufrag、a=ice-pwd)、DTLS 证书的指纹(a=fingerprint)、DTLS 角色(a=setup:actpass、active、passive),以及候选(a=candidate)。应答方把收到的提议放入 setRemoteDescription,再用 createAnswer 作答。

ICE 候选。ICE(RFC 8445) 会收集所有可能的地址(候选收集),将它们配对并做连通性检查,然后从可行的配对中选出一个。候选有三种。

种类 来自哪里 含义
host 自己的接口 在同一网络内,用它就足够了
srflx STUN 服务器看到的地址 NAT 转换后的外部地址
relay TURN 服务器借出的地址 无法直接到达时,由服务器代为传输

STUN(RFC 8489) 服务器只是对 Binding 请求,用 XOR-MAPPED-ADDRESS 返回“我看到的你的地址”,并不传输数据。在对称 NAT 或 UDP 被封的网络中,即使有这个地址也无法到达,所以 TURN(RFC 8656) 服务器会借出中继地址,并代为传输所有数据。TURN 会产生带宽成本,但如果没有它,有些用户就根本连不上。为了 UDP 被封的地方,也会通过 TCP、TLS 443 开放 TURN。有两种方式:收集完所有候选之后再发送 SDP,以及一边收集一边分别发送的 trickle ICE。本实验的 aiortc 采用前一种方式,所以如果配置了无法到达的 STUN 服务器,提议就会因等待其响应而推迟几秒。

DTLS-SRTP 与 SCTP。路径确定之后,就在其上进行 DTLS 握手。对方出示的证书的哈希,必须与通过信令收到的 a=fingerprint 相同,这样才能防止中间人攻击。RFC 8827 要求对所有媒体加密,媒体密钥从 DTLS 握手中导出并用于 SRTP(RFC 5764)。数据通道通过 DTLS 之上的 SCTP 传输(RFC 8831),每个通道可以分别确定是否保证顺序,以及重传上限(maxRetransmits)或寿命(maxPacketLifeTime)。对于像语音帧这样过了 20ms 就没用的数据,最好不保序、不重传地发送。

媒体轨道。音频通过 RTP 传输。RFC 7874 规定 WebRTC 必须实现的音频编解码器是 Opus 和 G.711,RFC 7587 要求无论输入采样率是多少,都在 SDP 中把 Opus 写成 opus/48000/2。

在现场相遇的样子

抖动与丢包。语音没有等待重传的余地,所以接收方用抖动缓冲吸收到达间隔的波动,并用前后的声音填补丢失的帧(丢包隐藏)。缓冲区越大,卡顿越少,但所有声音也会相应地推迟。ITU-T G.114 认为单向延迟在 150ms 以下,对大多数对话都没有问题。“只有在公司网络里才连不上”,通常是 UDP 被封,而又没有 TURN,或者 TURN 只通过 UDP 开放;“连接要 5 秒”,则是在等待无法到达的 STUN。

下一项实验要做什么

构建 WebSocket 信令服务器,并用 aiortc 创建应答对等端和提议对等端,通过数据通道与基准对等端收发消息。把通道改为不保序、不重传,并测量在同一个 Pod 内的 STUN 服务器上产生 srflx 候选的情形,以及被封的 STUN 拖慢收集的情形。