WebRTC 探险记:我是如何让两个浏览器"看对眼"的

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 公里的距离上,实时看到对方的脸。

这不是魔法,这是工程。

相关推荐
XIAEE1 小时前
ES6 与 Node.js 群侠传:那些年我们追过的语法,和那些年把我们追杀的 API
java·spring
XIAEE1 小时前
UniApp 本地打包通关记:Android 是地狱难度,iOS 是隐藏 BOSS
java·spring
高晶1 小时前
Android 物联网组网实战:NFC 从零到落地
android·java
Dreamboat-L2 小时前
日志级别基础 + Spring Boot 日志实践指南
java·服务器·数据库·java-ee·springboot
啵啵啵鱼2 小时前
第二章 线性表
java·数据结构·算法
深入云栈2 小时前
Netty 4.2.x 源码深度解析 (十九):ChannelPool 连接池 —— 连接复用与资源管理
java·后端·架构
用户094248568032 小时前
第38章:从零构建JVM稳定性平台最小闭环
java·jvm
Zhuo1Joy2 小时前
压测全绿的假象:0% 错误率的系统与 17.2% 成功率的业务
java
silvamux_2 小时前
Spring Boot 调用大模型 api 最小可跑教程
java·spring boot·后端