本文目录
两个人想打视频电话,先要交换联系方式,再确认彼此能否直连;如果两边的网络都藏在门禁后面,还需要一个中转站。WebRTC(Web Real-Time Communication,网页实时通信)把摄像头、麦克风和数据通道带进浏览器,但它并不负责替你完成“找到对方”这一步。
先看一张通信地图
信令服务器通常只交换 SDP(会话描述)和 ICE candidate(候选网络地址),真正的音视频数据建立后可以不经过信令服务器。
RTCPeerConnection 做了什么
RTCPeerConnection 负责协商能力、收集候选地址、建立加密连接和管理连接状态。它不等于 WebSocket:WebSocket 是浏览器与服务器之间的长连接,WebRTC 的媒体和数据通道目标是浏览器之间的实时传输。
const peer = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.example.com' }],
})
peer.onicecandidate = ({ candidate }) => {
if (candidate) sendToSignalServer({ candidate })
}
peer.ontrack = ({ streams }) => {
remoteVideo.srcObject = streams[0]
}一方创建 offer,另一方创建 answer,双方再通过信令服务器交换。这个过程不是“调用一个 API 就连上”,而是协商、探测和状态变化的组合。
ICE、STUN 和 TURN
ICE(Interactive Connectivity Establishment,交互式连接建立)会收集多种候选路径并尝试连通。STUN 服务器帮助浏览器发现自己在公网看到的地址;当两端无法直接穿透 NAT(网络地址转换)时,TURN 服务器作为中继转发媒体。
因此“点对点”并不保证所有数据都不经过服务器。能直连时更省资源,必须中继时则依赖 TURN 的带宽和部署质量。
摄像头权限与媒体轨道
getUserMedia 需要安全上下文和用户授权,返回 MediaStream。把轨道添加到连接后,远端才有机会收到它。结束通话时要停止轨道、关闭连接并清理本地视频,否则摄像头指示灯和资源可能仍然保持占用。
连接状态不是一个布尔值
RTCPeerConnection 会经历 new、connecting、connected、disconnected、failed 和 closed 等状态。disconnected 可能只是短暂的网络抖动,failed 才表示当前候选路径已经无法继续。应用应把状态展示给用户,并设计重新协商或重新建立连接的策略,而不是只在第一次连接成功时写一套逻辑。
媒体轨道和数据通道也有自己的状态。视频可能还连着,但摄像头轨道已经被用户关闭;数据通道可能打开,却因为发送缓冲区增长而产生延迟。bufferedAmount、getStats() 和轨道事件能帮助你区分“连接断了”和“连接还在但质量下降”。
信令是应用层协议
WebRTC 没有规定信令服务器必须使用 WebSocket、HTTP 还是其他协议。你的应用需要决定房间、用户身份、offer/answer 保存时间、候选转发和重连规则。信令消息应带有会话 id,防止旧连接的 candidate 混入新会话;用户离开房间时也要通知对方关闭资源。
安全上,摄像头和麦克风权限应尽量在用户明确操作后请求,信令服务要校验房间成员,远端媒体也不应被默认当作可信内容。实时通信的难点不只是“能不能连上”,还包括谁能加入、连接变差时怎么退化,以及离开后是否真的释放设备。
一个成熟的实时功能通常还要准备降级路径:摄像头不可用时只保留音频,带宽不足时降低分辨率或帧率,连接失败时退回普通 HTTP 轮询或文字聊天。这样的设计能把 WebRTC 当作增强能力,而不是让它成为页面唯一的可用路径。排查问题时也要同时记录浏览器版本、网络类型、候选类型、往返时间和丢包率,否则“偶尔卡”很难被还原。
如果页面只需要传递少量实时状态,RTCDataChannel 也不一定是最简单的选择。先明确消息是否必须可靠、是否必须有序,以及旧消息能否丢弃。聊天消息通常需要可靠有序,实时光标或游戏位置则可能更在意最新值。通信模型先于 API 选择,只有把这层约束说清楚,才能调节重传、缓冲和延迟之间的取舍。
媒体通话还要处理设备切换和页面可见性。用户插入耳机、关闭摄像头或切换到后台时,应用应更新轨道和界面状态,而不是继续假设最初的设备一直存在。清晰地区分“权限被拒绝”“设备不存在”“网络失败”和“对方主动离开”,用户才能知道下一步该做什么。
排查 WebRTC 时,先确认信令是否完成,再确认 ICE 是否找到候选路径,最后看媒体轨道和统计数据。把这三层混在一起,往往会把“没有交换 answer”和“网络丢包”误认为同一个问题。
这条排查顺序也适合写进日志:会话创建、权限结果、候选类型、连接状态、轨道状态和关闭原因。实时系统很难只靠用户一句“刚才卡了一下”定位,时间线比截图更有价值。
先把连接过程看懂,再谈码率和清晰度;否则优化只是在猜。
浏览器实时通信的工程重点,始终是连接、权限、质量和退出都可观察、可恢复——这比单纯展示一个“通话中”标签更接近真实产品。先保证可恢复,再追求低延迟。
容易踩的坑
WebRTC 仍需要信令交换;复杂网络里还得靠 STUN/TURN 打洞或中继,并不是“完全没有服务器”。连接建立后也会因网络切换、设备变化和带宽波动而重协商。它也不只传视频:RTCDataChannel 可以传文本与二进制,但仍要自己处理顺序、可靠性与拥塞。