跳到主要内容

WebRTC 全景实战 (5):ICE、STUN、TURN 与 NAT 穿透

Rainy
雨落无声,代码成诗 —— 致力于技术与艺术的极致平衡
Rainy
24 MIN READ... VIEWS

"70% 的 WebRTC bug 与 NAT 有关。" — 业界经验总结

Ch4 SDP 中的 a=ice-ufraga=ice-pwd 只是凭证;真正让两个浏览器在 NAT 后面找到彼此、建立 UDP 通道的,是 ICE(Interactive Connectivity Establishment)——RFC 8445 定义的标准算法。

1990 年代 MBONE 多播时代,发送者只需向多播组发一次包,路由器负责复制给所有订阅者。Ron Frederick 在 WebRTC for the Curious — 历史 中回忆:他和同事都是 IP 多播研究者,用 nv 工具向整个 Internet 广播会议视频——一份数据包,数百个子网同时接收。Serge Lachapelle 则描述了另一条演进路线:他创办的 Marratech 最初也依赖多播网络,「服务器可以非常简单,因为网络负责把视频包复制给通话中的每一个人」——但「必须设计网络以适配多播模式」这一致命缺点,最终推动行业从多播转向 SFU(packet shufflers)。

WebRTC 运行在没有多播的公网上。IPv4 地址耗尽催生了 NAT 的大规模部署,每个参与者必须找到与对端通信的具体 IP:Port 路径。从「一对多广播」到「点对点单播」的设计转折,是理解 ICE 存在原因的关键背景。

本章深入 ICE 状态机、Candidate 类型、STUN/TURN 协议细节、Trickle ICE 优化,以及生产环境中最常见的穿透失败排查路径。

配套 Lab:examples/webrtc-lab/docker/coturn/ + client/ch02-p2p-basic/


本篇术语表

术语英文解释
ICEInteractive Connectivity Establishment在多个 Candidate 地址之间做连通性检查,选出最优传输路径的标准算法
CandidateICE Candidate一个可用的网络地址(IP:Port + 类型 + 优先级),供 ICE 检查
hostHost Candidate本机网卡直接绑定的地址,如 192.168.1.10:54321
srflxServer Reflexive经 STUN 服务器反射得到的公网映射地址
prflxPeer Reflexive连通性检查过程中动态发现的远端映射地址
relayRelay CandidateTURN 服务器分配的中继地址,P2P 失败时的兜底
STUNSession Traversal Utilities for NATRFC 5389,帮助客户端发现自己的公网映射
TURNTraversal Using Relays around NATRFC 5766,在服务端分配中继地址转发流量
Connectivity CheckICE 用 STUN Binding Request 探测 Candidate Pair 是否可达
Nomination提名ICE 选出「最终使用」的 Candidate Pair 的过程
Trickle ICE边收集 Candidate 边通过信令发送,而非等全部收集完
NATNetwork Address Translation将私网地址映射到公网地址/端口的中间设备
Symmetric NAT每个目标地址分配不同外部端口,P2P 几乎必然失败
CGNATCarrier-Grade NAT运营商级 NAT,多层 NAT 嵌套,穿透难度更高
ice-ufrag / ice-pwdSDP 中的 ICE 凭证,用于连通性检查的身份验证
Candidate Pair本地 Candidate + 远端 Candidate 的组合,ICE 逐个检查
iceTransportPolicyall(默认)或 relay(强制走 TURN)
ICE-CONTROLLING连通性检查中拥有提名权的一方(Offer 方通常为 controlling)
ICE-Lite简化版 ICE 实现,仅被动响应检查,不主动探测

一、从多播到单播:为什么 ICE 存在

Curious 历史章节 勾勒出一条清晰的技术演进线:

时代模型带宽效率网络要求
MBONE / nv (1992)IP 多播发送方只发一份全网支持多播
Marratech (2000s)多播 + 简单服务器企业网多播
SFU / WebRTC (2010+)单播 P2P 或 SFU 转发每跳一份仅需 UDP 出站

Ron Frederick 坦言:「有时我希望我们之前能更加努力地推动 IP 多播的应用……如果我们这么做了,可能早就可以看到有线电视过渡到基于 Internet 的音频和视频。」但现实是公网 ISP 几乎不转发多播,IPv4 地址耗尽又催生了 NAT 的大规模部署。

每个 WebRTC 参与者可能有多个网络接口(Wi-Fi、以太网、VPN、IPv6),每个接口经 NAT 映射后又有不同的公网地址。ICE 的任务就是:枚举所有可能的路径,逐个探测,选出延迟最低、可用的那条

Serge Lachapelle 在 Google 收购 Global IP Solutions(GIPS)后,把 VoIP 技术栈搬进浏览器——GIPS 的 libjingle 提供了 ICE/STUN/TURN 的成熟实现,这正是今天 Chrome 内置 ICE Agent 的根基。


二、ICE 在连接建立中的位置

WebRTC 连接建立是一个多层协议栈的叠加过程。ICE 位于 SDP 协商之后、DTLS 握手之前:

阶段协议本章是否涉及
信令WebSocket / 自定义间接(传递 Candidate)
媒体协商SDP Offer/AnswerCh4
地址发现与选路ICE + STUN + TURN本章
加密DTLSCh7
媒体传输SRTP / SCTPCh6/Ch8

ICE 不传输任何应用数据——它只负责在 UDP(偶尔 TCP)上找到一条可达的五元组 (srcIP, srcPort, dstIP, dstPort, UDP)。找到之后,DTLS 在同一五元组上握手,SRTP/SCTP 复用该通道。


三、ICE 状态机详解

浏览器通过 RTCPeerConnection.iceConnectionState 暴露 ICE 连接状态:

3.1 各状态含义

状态含义典型持续时间
new尚未开始 ICE 检查
checking正在对 Candidate Pair 做连通性检查数百 ms ~ 数秒
connected至少一个 Pair 成功,媒体可传输
completed提名完成,不再切换 Pair稳定连接后
disconnected临时断连,可能自动恢复数秒
failed所有 Pair 失败,需人工介入终止

3.2 iceGatheringState 与 connectionState

ICE 涉及三个平行的状态维度,调试时缺一不可:

属性反映层次关键值
iceGatheringStateCandidate 收集进度newgatheringcomplete
iceConnectionStateICE 连通性checkingconnected / failed
connectionStateICE + DTLS 整体connectingconnected / failed
const pc = new RTCPeerConnection({ iceServers: ICE_SERVERS });

pc.oniceconnectionstatechange = () => {
const state = pc.iceConnectionState;
console.log("[ICE]", state);

switch (state) {
case "checking":
console.log("正在探测 Candidate Pair…");
break;
case "connected":
case "completed":
console.log("ICE 连通,DTLS 应已开始");
break;
case "disconnected":
console.warn("临时断连,等待恢复或触发 failed");
break;
case "failed":
console.error("ICE 失败 — 检查 TURN 配置、防火墙、Candidate 类型");
break;
}
};

pc.onicegatheringstatechange = () => {
console.log("[Gathering]", pc.iceGatheringState);
};

pc.onconnectionstatechange = () => {
console.log("[Connection]", pc.connectionState);
// connectionState=failed 但 iceConnectionState=connected → DTLS 问题,见 Ch7
};

pc.onicecandidate = (event) => {
if (event.candidate) {
signaling.send({ type: "candidate", candidate: event.candidate });
} else {
console.log("ICE gathering complete (null candidate)");
}
};
iceConnectionState vs connectionState

iceConnectionState 只反映 ICE 层;connectionState 还包含 DTLS 状态。调试时两者都要看——可能出现 ICE connectedconnectionState=failed(DTLS 证书/指纹不匹配)。


四、Candidate 类型与优先级

ICE 为每个 Candidate 计算优先级,host > srflx > relay(同类型内还有协议、接口偏好等细粒度排序):

4.1 Candidate SDP 格式

a=candidate:842163049 1 udp 2130706431 192.168.1.10 54321 typ host
a=candidate:842163049 2 udp 2130706431 192.168.1.10 54321 typ host
a=candidate:1234567890 1 udp 1694498815 1.2.3.4 54321 typ srflx raddr 192.168.1.10 rport 54321
a=candidate:9876543210 1 udp 16777215 5.6.7.8 60000 typ relay raddr 1.2.3.4 rport 54321
a=candidate:1111111111 1 udp 2130706431 abcd1234-5678-90ab.local 54321 typ host
字段示例含义
foundation842163049相同类型+地址的 Candidate 共享 foundation
component1 / 21=RTP,2=RTCP(WebRTC 用 rtcp-mux 时通常只有 1)
transportudp / tcp传输协议
priority2130706431优先级数值,越大越优先
address192.168.1.10IP 地址
port54321端口
typhost / srflx / relay / prflxCandidate 类型
raddr / rportsrflx/relay 附带本地映射前的地址

4.2 优先级计算公式(RFC 8445)

优先级是一个 32 位无符号整数,公式为:

priority = (2^24) × (126 - typePreference)
+ (2^8) × (256 - localPreference)
+ (256 - componentId)
typePreference类型
host本机126
prflx对端反射110
srflx服务器反射100
relay中继0

因此 host candidate 的 priority 天然高于 srflx,srflx 高于 relay。浏览器自动计算,开发者通常无需手动干预。

4.3 mDNS Host Candidate

Chrome 从 M94 起默认用 mDNS 隐藏本地 IP:host candidate 的地址显示为 xxxx-xxxx.local 而非真实 192.168.x.x。这是隐私保护特性,不影响 srflx/relay 的收集和 ICE 选路。在 chrome://webrtc-internals 中你会看到这类 candidate,属于正常现象。

4.4 配置 iceServers

const ICE_SERVERS = [
{ urls: "stun:stun.l.google.com:19302" },
{ urls: "stun:stun1.l.google.com:19302" },

{
urls: [
"turn:turn.example.com:3478?transport=udp",
"turn:turn.example.com:3478?transport=tcp",
"turns:turn.example.com:5349?transport=tcp",
],
username: "webrtc-user",
credential: "secret-token",
credentialType: "password",
},
];

const pc = new RTCPeerConnection({
iceServers: ICE_SERVERS,
iceCandidatePoolSize: 4,
iceTransportPolicy: "all",
bundlePolicy: "max-bundle",
});
配置项作用
iceServersSTUN/TURN 服务器列表
iceCandidatePoolSizecreateOffer 前预收集 N 个 Candidate,加速首连
iceTransportPolicy: "relay"隐私模式或对称 NAT 测试,禁用 host/srflx
bundlePolicy: "max-bundle"所有 m-line 复用同一 ICE 传输(WebRTC 默认行为)

五、STUN 协议深度解析(RFC 5389)

STUN 的核心消息只有几种:Binding Request / Binding Response,用于地址发现。Allocate / Send / Data 等属于 TURN 扩展(RFC 5766)。

5.1 Binding 交互时序

5.2 STUN 消息结构(简化)

RFC 5389 固定头 20 字节,后跟可变长 Attributes(TURN Allocate 等复用同一头部格式)。首 16 bit 的 Message Type 按位域编码(§6.1):

Class (C0C1)典型消息
00RequestSTUN Binding Request、TURN Allocate
01IndicationTURN Send/Data Indication
10Success ResponseBinding Success Response
11Error Response401 Unauthorized 等

Method 12 bit 标识具体操作,例如 Binding = 0x001Allocate = 0x003(TURN)。Class + Method 组合成线上 16 bit Message Type 字段——前两 bit 恒为 0 是 STUN 与旧版 STUN 的兼容标记,不可省略。

常见 Attribute:

Attribute用途
XOR-MAPPED-ADDRESS客户端的公网映射地址(防 NAT 篡改)
USERNAME / MESSAGE-INTEGRITYICE 连通性检查时的身份验证
PRIORITY / USE-CANDIDATEICE 提名与优先级
ICE-CONTROLLED / ICE-CONTROLLING决定哪端执行提名

5.3 ICE 连通性检查中的 STUN

Candidate 收集阶段的 STUN Binding 不带 ICE 凭证;连通性检查阶段的 STUN Binding 则携带 SDP 中的 ice-ufragice-pwd

  • ICE-CONTROLLING(通常为 Offer 方):有权发送 USE-CANDIDATE 提名
  • ICE-CONTROLLED(通常为 Answer 方):被动响应,接受提名

5.4 公共 STUN 服务器

提供者URL备注
Googlestun:stun.l.google.com:19302免费,无 SLA
Cloudflarestun:stun.cloudflare.com:3478免费
自建stun:your-domain:3478生产推荐(coturn 同时提供 STUN+TURN)
STUN 不转发媒体

STUN 只回答「你的公网地址是什么」。它不参与 RTP/SCTP 数据转发。如果 P2P 打洞失败,必须靠 TURN。


六、TURN 中继协议(RFC 5766)

当双方都是 Symmetric NAT,或企业防火墙只允许出站 UDP 时,P2P 必然失败。TURN 在服务端分配中继地址(relay candidate),所有媒体经服务器转发。

6.1 TURN Allocate 时序

6.2 TURN 认证机制

生产 TURN 通常使用 长期凭证(long-term credentials)REST API 临时凭证(Twilio、Xirsys 模式):

// 长期凭证(开发/内网)
{
urls: "turn:turn.example.com:3478",
username: "test",
credential: "test123",
}
// REST 临时凭证(生产推荐)—— 服务端生成
const crypto = require("crypto");

function generateTurnCredentials(secret, ttl = 86400) {
const timestamp = Math.floor(Date.now() / 1000) + ttl;
const username = `${timestamp}:userId123`;
const hmac = crypto.createHmac("sha1", secret);
hmac.update(username);
const credential = hmac.digest("base64");
return { username, credential };
}

// 客户端消费
const { username, credential } = await fetch("/api/turn-credentials").then(r => r.json());
const pc = new RTCPeerConnection({
iceServers: [{
urls: "turn:turn.example.com:3478",
username,
credential,
}],
});

临时凭证的优势:即使泄露,凭证在 TTL 后自动失效;可按用户/会话隔离。

6.3 coturn Docker 本地部署

examples/webrtc-lab/docker/coturn/docker-compose.yml

services:
coturn:
image: coturn/coturn:latest
ports:
- "3478:3478/udp"
- "3478:3478/tcp"
- "49152-49200:49152-49200/udp"
command: >
-n --log-file=stdout
--lt-cred-mech
--user=test:test123
--realm=webrtc.lab
--min-port=49152
--max-port=49200

启动与验证:

cd examples/webrtc-lab/docker/coturn
docker compose up -d

# 验证 STUN 响应(需安装 stuntman)
stunclient localhost 3478

# 查看 coturn 日志
docker compose logs -f coturn

浏览器端配置:

const LOCAL_TURN = {
urls: [
"turn:localhost:3478?transport=udp",
"turn:localhost:3478?transport=tcp",
],
username: "test",
credential: "test123",
};

const pc = new RTCPeerConnection({
iceServers: [
{ urls: "stun:stun.l.google.com:19302" },
LOCAL_TURN,
],
});

部署成功后,在 chrome://webrtc-internals 中应能看到 typ relay 的 Candidate。生产集群扩展见 Ch14 TURN 集群

6.4 TURN 带宽成本

模式路径服务器带宽
P2P (srflx)A ↔ B 直连0
TURN relayA → TURN → B发送量 × 2
SFU每人 → SFU → 每人N × 发送量

TURN 是兜底方案,但大流量场景(视频会议、文件传输)中 relay 比例直接影响成本。监控 relay 使用率是生产运维的关键指标。


七、Trickle ICE vs Full ICE

早期 ICE 实现等所有 Candidate 收集完毕才交换 SDP,首连延迟高。Trickle ICERFC 8838)边收集边发送,是现代 WebRTC 的标准做法。

模式行为首连延迟信令复杂度
Full ICEiceGatheringState=complete 再发 SDP较慢(2~5s)
Trickle ICEonicecandidate 实时转发快(<1s 常见)
ICE Restart断连后 restartIce() 重新收集取决于网络

examples/webrtc-lab/client/ch02-p2p-basic/ 使用 Full ICE(waitIceGatheringComplete),适合理解流程;生产信令服务器(Ch3)应实现 Trickle ICE。

SDP 中 a=ice-options:trickle 表示支持 Trickle ICE。信令层需处理 candidate 消息在 Offer/Answer 之前或之后到达的乱序情况:

const pendingCandidates = [];

async function onRemoteCandidate(candidate) {
if (pc.remoteDescription) {
await pc.addIceCandidate(candidate);
} else {
pendingCandidates.push(candidate);
}
}

async function onRemoteAnswer(answer) {
await pc.setRemoteDescription(answer);
for (const c of pendingCandidates) {
await pc.addIceCandidate(c);
}
pendingCandidates.length = 0;
}
addIceCandidate 的 end-of-candidates

Trickle ICE 结束时,发起方会发送 candidate: null(即 onicecandidateevent.candidate === null)。现代浏览器不再需要显式调用 addIceCandidate(null),但信令协议应能识别这一信号。


八、NAT 类型与穿透策略

RFC 4787 定义了 NAT 行为分类。理解 NAT 类型有助于预测 P2P 成功率:

NAT 类型映射行为过滤行为P2P 成功率
Full Cone固定映射任何源可入
Restricted Cone固定映射仅曾发送过的 IP
Port Restricted固定映射仅曾发送过的 IP:Port中低
Symmetric每个目标不同映射严格极低,必须 TURN

8.1 CGNAT(运营商级 NAT)

移动网络(4G/5G)和许多家庭宽带使用 CGNAT:你的「公网 IP」其实是运营商内网地址,外面还有一层 NAT。这意味着:

  • srflx candidate 拿到的是运营商 NAT 的外部地址,不是真正的公网 IP
  • 两层 NAT 叠加,打洞成功率进一步下降
  • 始终配置 TURN 是移动场景的生产标配

8.2 现实建议

不要试图在客户端检测 NAT 类型——结果不可靠且各浏览器实现不同。生产环境的正确策略是:

  1. 始终配置 STUN + TURN
  2. 让 ICE 自动选路
  3. 监控 relay 使用率,优化 TURN 集群部署(Ch14)
  4. 对隐私敏感场景,可用 iceTransportPolicy: "relay" 隐藏真实 IP

九、Candidate Pair 状态机

ICE 为每对 (local, remote) candidate 维护独立状态:

Pair 状态含义
frozen初始状态,等待解冻
waiting已解冻,排队等待检查
in-progress正在发送 STUN Binding
succeeded检查成功,可被提名
failed检查失败
nominated被选为最终传输路径

ICE Agent 按优先级从高到低逐个检查 Pair。第一个成功的 host-host 或 srflx-srflx Pair 通常延迟最低,会被优先提名。


十、ICE 失败诊断决策树

iceConnectionState === "failed" 时,按以下决策树排查:

10.1 常见失败原因

现象根因修复
只有 host,无 srflxSTUN 服务器不可达或被墙换 STUN / 检查出站 UDP
srflxrelay未配置 TURN 或认证失败检查 username/credential
relay 但仍 failedTURN 端口范围被防火墙拦截开放 49152-65535/UDP
本地通、跨网 failed对称 NAT 或运营商 CGNAT必须 TURN
连上后频繁 disconnectedWi-Fi 切换 / 网络抖动ICE Restart + 断线重连
mDNS host candidateChrome 隐私特性 .local正常,不影响 srflx/relay
TURN 凭证过期REST 临时凭证 TTL 到期重新获取凭证 / 延长 TTL

10.2 chrome://webrtc-internals 使用指南

  1. 打开 chrome://webrtc-internals(连接建立前打开,可捕获完整过程)
  2. 建立 WebRTC 连接
  3. 选择对应的 PeerConnection
  4. 查看 Statscandidate-pair → 找 selected=truestate=succeeded 的行
  5. 查看 ICE candidate grid → 确认本地/远端 Candidate 类型
  6. 点击 Create Dump 导出完整 JSON 用于离线分析

关键字段:

字段含义
selected / nominated是否为当前选中 Pair
localCandidateTypehost / srflx / relay / prflx
remoteCandidateType对端 Candidate 类型
bytesSent / bytesReceived确认有实际流量
currentRoundTripTimeRTT 估算
statePair 状态(succeeded / failed / in-progress)
async function exportIceDiagnostics(pc) {
const stats = await pc.getStats();
const report = {
iceConnectionState: pc.iceConnectionState,
iceGatheringState: pc.iceGatheringState,
connectionState: pc.connectionState,
candidates: { local: [], remote: [] },
pairs: [],
selectedPair: null,
};

stats.forEach((s) => {
if (s.type === "local-candidate") {
report.candidates.local.push({
type: s.candidateType,
address: s.address || s.ip,
port: s.port,
protocol: s.protocol,
});
}
if (s.type === "remote-candidate") {
report.candidates.remote.push({
type: s.candidateType,
address: s.address || s.ip,
port: s.port,
});
}
if (s.type === "candidate-pair") {
report.pairs.push({
state: s.state,
nominated: s.nominated,
bytesSent: s.bytesSent,
bytesReceived: s.bytesReceived,
rtt: s.currentRoundTripTime,
});
if (s.selected || s.nominated) {
report.selectedPair = s;
}
}
});

console.log(JSON.stringify(report, null, 2));
return report;
}

十一、ICE Restart 与断线恢复

网络切换(Wi-Fi → 4G)或 NAT 映射过期时,ICE 可能从 connected 变为 disconnected 甚至 failed。WebRTC 支持 ICE Restart 在不重建整个 PeerConnection 的情况下重新收集 Candidate:

async function handleIceFailure() {
if (pc.iceConnectionState !== "failed") return;

const offer = await pc.createOffer({ iceRestart: true });
await pc.setLocalDescription(offer);
signaling.send({ type: "offer", sdp: offer, iceRestart: true });
}

async function onIceRestartOffer(offer) {
await pc.setRemoteDescription(offer);
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
signaling.send({ type: "answer", sdp: answer });
}

ICE Restart 会生成新的 ice-ufrag / ice-pwd,旧 Candidate 全部作废。信令层需重新交换 Candidate。配合 Ch3 的重连状态机,这是生产系统断线恢复的标准手段。

disconnected 不等于 failed

disconnected 状态可能持续数秒后自动恢复为 connected(如短暂网络抖动)。不要立即触发 ICE Restart——建议等待 5~10 秒,确认无法恢复后再重启。


十二、实战 Lab

Lab 1:仅 STUN,观察 Candidate 类型

  1. 启动 examples/webrtc-lab/signaling/ 信令服务器(或直接用 ch02 手动交换 SDP)
  2. 打开两个 Tab 运行 client/ch02-p2p-basic/
  3. iceServers 只配 Google STUN,不配 TURN
  4. 打开 chrome://webrtc-internals,确认出现 host + srflx relay
  5. 记录 selected pair 的类型和 RTT

Lab 2:部署 coturn,验证 relay

  1. cd examples/webrtc-lab/docker/coturn && docker compose up -d
  2. 客户端添加 localhost TURN 配置(见第六节)
  3. 重新建立连接,确认出现 typ relay Candidate
  4. 对比 selected pair:P2P 成功时通常选 srflx

Lab 3:强制 TURN 中继

const pc = new RTCPeerConnection({
iceServers: [LOCAL_TURN],
iceTransportPolicy: "relay",
});
  1. 设置 iceTransportPolicy: "relay"
  2. 建立连接,确认 selected pair 的 local/remote 类型均为 relay
  3. 测量 RTT,对比 P2P 模式下的差异

Lab 4:Trickle ICE 时序观察

  1. onicecandidate 中加 performance.now() 时间戳日志
  2. 对比各 Candidate 到达顺序 vs iceConnectionState 变化时间
  3. 验证:第一个 srflx candidate 到达后,checking 状态是否很快出现

Lab 5:故障注入

实验操作预期
无 TURN移除 TURN 配置大多数家庭网络仍可 P2P
错误 TURN 密码credential 故意写错无 relay candidate,coturn 日志显示认证失败
阻断 UDP防火墙禁出站 UDPICE failed
强制 relayiceTransportPolicy: "relay"只有 relay pair 成功
错误 STUN 地址urls 指向不存在的主机无 srflx,仅 host candidate

Lab 6:导出诊断报告

  1. 在连接失败时调用 exportIceDiagnostics(pc)
  2. 检查 candidates.local 中是否有 relay 类型
  3. 检查 pairs 中是否有 state: "succeeded" 的条目
  4. 将 JSON 报告与 chrome://webrtc-internals 的 Dump 交叉验证

下一篇(Ch6)Data Channel 与 SCTP over DTLS——ICE 连通后,如何在 DTLS 之上传输任意 P2P 数据。


十三、本章小结

概念要点
ICE枚举 + 检查 + 提名,RFC 8445
STUN发现公网映射,RFC 5389,不转发数据
TURN中继兜底,RFC 5766,带宽成本 ×2
Trickle ICE边收集边发送,降低首连延迟
生产策略始终 STUN + TURN,监控 relay 比例

Phase 2(连接建立)继续深入:Ch6 Data Channel 将在 ICE 选出的路径上传输任意 P2P 数据。


系列导航

章节主题状态
0架构全景与协议栈地图✅ 已发布
1浏览器媒体 API 与设备管理✅ 已发布
2第一个 P2P 视频通话✅ 已发布
3信令服务器设计与会话状态机✅ 已发布
4SDP 解剖与媒体协商✅ 已发布
5ICE、STUN、TURN 与 NAT 穿透✅ 已发布
6Data Channel 与 SCTP over DTLS✅ 已发布
7DTLS 握手与 SRTP 加密体系✅ 已发布
8RTP/RTCP 媒体传输与 QoS✅ 已发布
9音视频编解码与 Simulcast 入门✅ 已发布
10带宽估计与拥塞控制 GCC✅ 已发布
11Simulcast、SVC 与选择性订阅✅ 已发布
12SFU/MCU/Mesh 架构与 Pion 实战✅ 已发布
13调试工具链与可观测性✅ 已发布
14TURN 集群部署与多区域扩展✅ 已发布
15Capstone 生产级视频会议系统✅ 已发布

References

Logo
RainLib

探索技术、设计与分布式系统的边界。构建面向未来的开发者工具。

留言与建议

© 2026 RainLib. 为未来构建。(Built for the Future)
版权所有。
系统正常