
WebRTC 的工作流程
WebRTC 的工作流程主要包括以下几个步骤:
- 媒体捕获:使用 MediaStream API 捕获音频和视频。
- 信令:WebRTC 本身不指定信令协议,但需要开发者实现自己的信令机制(通常使用 WebSocket 或 REST API)来交换元数据,如 SDP 描述符和 ICE 候选。信令服务器负责发现对方、管理房间信息,并转发媒体和网络信息。
- 建立连接:通过 RTCPeerConnection 建立一个对等(Peer-to-Peer)连接。
- 媒体协商:通过 SDP 交换彼此的音视频编解码能力,确保双方能正确编解码。
- NAT 穿透:利用 ICE 框架,通过 STUN 服务器发现公网地址,若失败则通过 TURN 服务器中继流量,以应对不同的 NAT 类型。ICE 候选者有优先级之分,包括主机候选者(本地局域网 IP)、反射候选者(NAT 后的外网 IP)和中继候选者(TURN 服务器中转地址)。
- 数据交换:RTCDataChannel 允许对等端之间通过建立的连接交换数据。
流程图:端到端连接建立概览
flowchart LR
A[本地浏览器\ngetUserMedia 捕获媒体] --> B[信令通道\n使用 WebSocket 或 HTTP]
A --> C[创建 RTCPeerConnection]
C --> D[创建 SDP Offer]
D -->|通过信令发送| E[远端浏览器]
E --> F[设置远端 SDP\n并创建 Answer]
F -->|通过信令返回| C
C --> G[ICE 候选收集\n含 STUN 查询]
G -->|发送候选| E
E -->|回传候选| C
C --> H{能否直连}
H -->|是| I[建立 P2P 直连\n媒体与数据直传]
H -->|否| J[通过 TURN 中继\n建立连接]
I --> K[媒体流与数据通道\n稳定传输]
J --> K
以上流程图概览了从本地媒体采集、SDP 协商、ICE/STUN/TURN 选择到最终媒体/数据传输的关键步骤。
时序图:SDP/ICE 信令交换
sequenceDiagram
%% SDP/ICE 详细时序
participant L as 本地浏览器
participant S as 信令服务器
participant R as 远端浏览器
Note over L: getUserMedia() 获取音视频轨道
L->>L: new RTCPeerConnection()
L->>L: addTrack()/addTransceiver()
L->>L: createOffer() / setLocalDescription(offer)
L->>S: 发送 SDP Offer
S->>R: 转发 SDP Offer
R->>R: setRemoteDescription(offer)
R->>R: createAnswer() / setLocalDescription(answer)
R->>S: 发送 SDP Answer
S->>L: 转发 SDP Answer
L->>L: setRemoteDescription(answer)
par ICE 候选交换
L->>L: 收集本地 ICE 候选(含 STUN 外网候选)
L->>S: 发送 ICE 候选
S->>R: 转发 ICE 候选
R->>S: 回传 ICE 候选
S->>L: 转发 ICE 候选
end
alt 直连成功
L-->>R: 建立 P2P 媒体/数据通道
else 直连失败
L-->>R: 通过 TURN 中继完成连接
end
上述时序图聚焦于通过信令服务器交换 SDP 与 ICE 候选的过程,展示了典型的一次通话建立所需的关键往返。
更直白的理解
信令 / SDP
- 信令是“打招呼”和“约定怎么聊”。
- SDP 就是“我能说什么、怎么看视频”的清单(编解码器、分辨率等)。
ICE / STUN / TURN
- 先走直连,走不通就查公网地址(STUN),再不行走中转(TURN)。
- 直连延迟低;中转更稳但贵。
媒体 vs 数据通道
- 音视频像直播;
- DataChannel 像对讲机或快递:要么快一点、丢点包没事;要么严格到齐、顺序到达。
最小示例
仅演示流程,真实项目需要信令服务端。
// 关键步骤示例
const pc = new RTCPeerConnection();
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
stream.getTracks().forEach(t => pc.addTrack(t, stream));
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
signaling.send({ type: 'offer', sdp: offer.sdp });
pc.onicecandidate = (e) => e.candidate &&
signaling.send({ type: 'candidate', candidate: e.candidate });
signaling.on('answer', async ({ sdp }) =>
pc.setRemoteDescription({ type: 'answer', sdp })
);
const dc = pc.createDataChannel('chat', { ordered: false });
dc.onopen = () => dc.send('hi');
标签
版权声明
本文由 WebRTC.link 创作,采用 CC BY-NC-SA 4.0 许可协议。本站转载文章会注明来源以及作者。如果您需要转载,请注明出处以及作者。
评论区
Giscus评论由 Giscus 驱动,基于 GitHub Discussions