WebRTC 探险记:我是如何让两个浏览器"看对眼"的
你打开摄像头,对方也打开摄像头。你们之间隔着 8000 公里,但画面延迟只有 100 毫秒。这不是魔法,这是 WebRTC------一个让浏览器学会"面对面聊天"的技术。
序章:一个让我怀疑物理定律的需求
事情起源于一个再普通不过的需求:做个网页版视频通话。
我当时的想法很天真:不就是调用摄像头,把视频流传给对方吗?能有多难?
然后我遇到了第一个问题:视频流传给谁?
"当然是服务器啊。"
"那你服务器带宽多少?两个人同时在线,每人上传 2Mbps,服务器就要转发 4Mbps。100 个人同时在线呢?"
我沉默了。
就在我准备去算服务器成本的时候,有人跟我说了一句改变我认知的话:
"为什么要经过服务器?让两个浏览器直接连不就行了?"
浏览器直接连浏览器?这也能行?
那一刻,我打开了 WebRTC 的文档。一个全新的世界,在我面前展开。
第一章:WebRTC 是什么?------浏览器界的"隔空传功"
WebRTC,全称 Web Real-Time Communication ,翻译成人话就是:"网页实时通信"。
它的核心能力只有一个,但极其强大:
让两个浏览器,直接建立连接,互相传输音视频和数据,不需要经过服务器中转。
打个比方:
- 传统视频通话:你说话 → 服务器 → 对方听。服务器是"中间人"。
- WebRTC 视频通话:你说话 → 对方听。服务器只负责"介绍你们认识"。
就像相亲:婚介所(服务器)只负责牵线,牵完线你们自己聊,不用每句话都经过婚介所。
这就是 WebRTC 最核心的价值:P2P(Peer-to-Peer),点对点。
第二章:为什么需要"介绍人"?------信令服务器的秘密
你可能会问:既然浏览器能直接连,为什么还需要服务器?
因为两个浏览器要"直接连",得先知道对方在哪。
这就像打电话:你知道对方的手机号,才能拨过去。
但浏览器的"手机号"是什么?是 IP 地址和端口。而问题是:
- 你的浏览器在 NAT 后面,没有公网 IP。
- 对方的浏览器也在 NAT 后面,也没有公网 IP。
- 你们互相不知道对方的存在。
所以,需要一个"介绍人"来交换信息。
这个介绍人,叫 信令服务器(Signaling Server)。
它的工作流程大概是:
text
css
浏览器 A 信令服务器 浏览器 B
| | |
|--- 我想和 B 通话 --------->| |
| |<--- B 也想和 A 通话 -------|
| | |
|<-- 这是 A 的"联系方式" ----| |
| |--- 这是 B 的"联系方式" --->|
| | |
|<========= 直接 P2P 连接建立 =========>|
信令服务器不传视频、不传音频,它只传"联系方式"。
一旦连接建立,信令服务器就可以"下班"了。
第三章:SDP------那份"相亲简历"
现在问题来了:两个浏览器交换的"联系方式",到底是什么?
答案是:SDP(Session Description Protocol,会话描述协议)。
SDP 是一份"自我介绍",包含:
- 我支持什么编解码器(H.264?VP8?)。
- 我支持什么分辨率(720p?1080p?)。
- 我的网络地址是什么。
- 我支持什么传输协议。
它就像一份相亲简历:
text
css
姓名:浏览器 A
年龄:Chrome 120
特长:H.264, VP8, Opus
住址:192.168.1.100:54321
爱好:视频通话、数据传输
浏览器 A 和浏览器 B 交换简历,互相确认"我们合得来",然后才能开始通话。
这个交换过程,叫 Offer/Answer 模型:
- A 发送 Offer("我想这样通话,你行吗?")
- B 回复 Answer("行,就这样。")
如果两边都同意,通话就可以开始了。
第四章:ICE------那个让我抓狂的"打洞"过程
交换完 SDP,两个浏览器知道对方支持什么了。但还有一个更棘手的问题:
它们怎么找到对方?
因为 NAT 的存在,浏览器的"地址"不是真实的公网地址。你告诉对方 192.168.1.100:54321,对方根本访问不到------那是你局域网里的地址,出了门就没人认识。
这时候,ICE 出场了。
ICE = Interactive Connectivity Establishment,交互式连接建立。
它的工作是:找到一条能通的路径。
ICE 会尝试多种方式:
方式一:STUN 服务器------"查一下我的公网地址"
浏览器问 STUN 服务器:"我的公网地址是什么?"
STUN 回答:"你是 203.0.113.5:54321。"
浏览器把这个地址告诉对方,对方试着连。
如果运气好,NAT 是"锥形"的,这条路径能通。
方式二:TURN 服务器------"实在不行,我帮你转发"
如果 NAT 太严格,P2P 连不上,那就只能用 TURN 服务器中转。
TURN 服务器是一个"中继站":A 把数据发给 TURN,TURN 转发给 B。
这就不再是 P2P 了,而是"绕一圈"。 延迟会高,成本也高,但至少能通。
所以 WebRTC 的完整流程是:
text
markdown
1. 交换 SDP(信令服务器)
2. 收集 ICE 候选者(STUN/TURN)
3. 尝试连接(P2P 优先,TURN 兜底)
4. 连接建立,开始传输音视频
第五章:动手写一个最小的 WebRTC 通话
理论说完了,来点实际的。
两个浏览器建立连接的最小代码:
js
csharp
// 创建 RTCPeerConnection
const pc = new RTCPeerConnection({
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' }
]
})
// 获取本地摄像头
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })
stream.getTracks().forEach(track => pc.addTrack(track, stream))
// 显示本地视频
localVideo.srcObject = stream
// 收到对方视频
pc.ontrack = (event) => {
remoteVideo.srcObject = event.streams[0]
}
// A 创建 Offer
const offer = await pc.createOffer()
await pc.setLocalDescription(offer)
// 通过信令服务器发送 offer 给 B
signalServer.send({ type: 'offer', sdp: offer })
// B 收到 Offer 后
await pc.setRemoteDescription(offer)
const answer = await pc.createAnswer()
await pc.setLocalDescription(answer)
// 通过信令服务器发送 answer 给 A
signalServer.send({ type: 'answer', sdp: answer })
// A 收到 Answer 后
await pc.setRemoteDescription(answer)
// ICE 候选者交换
pc.onicecandidate = (event) => {
if (event.candidate) {
signalServer.send({ type: 'candidate', candidate: event.candidate })
}
}
// 对方收到 candidate
await pc.addIceCandidate(candidate)
就这么点代码,两个浏览器就能视频通话了。
当然,信令服务器得你自己实现。 用 WebSocket、Socket.io、甚至 HTTP 轮询都行,WebRTC 不关心你怎么传 SDP,它只关心最后能不能连上。
第六章:那些让我抓狂的坑
坑一:Chrome 和 Safari 的 SDP 不一样
Chrome 生成的 SDP,Safari 可能读不懂。因为两家对 SDP 的格式理解有差异。
解决办法: 用 webrtc-adapter,它会自动处理浏览器差异。
js
arduino
import 'webrtc-adapter'
坑二:ontrack 和 onaddstream
老版本用 onaddstream,新版本用 ontrack。
如果你发现 onaddstream 不触发,换成 ontrack 试试。
坑三:ICE 收集不完整
有时候 ICE candidate 还没收集完,就发出了 Offer。
解决办法: 等 icegatheringstate 变成 complete 再发。
js
ini
pc.onicegatheringstatechange = () => {
if (pc.iceGatheringState === 'complete') {
// 现在可以发送 Offer 了
}
}
坑四:移动端浏览器不支持
iOS 的 Safari 对 WebRTC 支持较晚,而且必须在 HTTPS 下才能调用摄像头。
所以,WebRTC 必须跑在 HTTPS 上。 本地开发可以用 localhost,线上必须配 SSL 证书。
第七章:WebRTC 还能做什么?
视频通话只是 WebRTC 的一个应用。它还能做:
- 屏幕共享:getDisplayMedia(),分享你的屏幕。
- 文件传输:用 DataChannel 传文件,P2P 直连,不经过服务器。
- 在线协作:多人实时编辑,类似 Google Docs。
- 云游戏:把游戏画面实时传到浏览器。
- 远程桌面:控制另一台电脑。
- 直播:低延迟直播,比 HLS 快得多。
WebRTC 的本质是"实时通信管道",视频通话只是管道里流的一种东西。
终章:回到最初的问题
现在,我可以回答开头那个问题了。
"浏览器直接连浏览器,这也能行?"
能行。而且这是 WebRTC 最迷人的地方。
它没有发明新的物理定律,它只是巧妙地利用了现有的网络协议:
- 用 SDP 交换能力。
- 用 ICE 打洞。
- 用 STUN 查地址。
- 用 TURN 兜底。
- 用 DTLS/SRTP 加密传输。
这一套组合拳打下来,两个素不相识的浏览器,就能在 8000 公里的距离上,实时看到对方的脸。
这不是魔法,这是工程。