你有没有想过:微信视频、腾讯会议为什么延迟那么低?两个人在不同的城市,怎么才能不经过服务器直接"喊到"对方?
今天,用对讲机的故事,来讲讲浏览器里的 P2P 通信。
原文地址
从打电话说起
传统电话是怎么打的?
想象一下,你要给远在北京的朋友打电话。
传统电话的流程是这样的:
yaml
你(北京) ──> 上海总机 ──> 北京总机 <── 朋友
↑
所有声音都要
经过总机转发
所有的声音数据,都要经过电话交换机的"总机"。如果总机挂了,所有人都会断线。
这有什么问题?
| 问题 | 举个例子 |
|---|---|
| 延迟高 | 声音绕远路,本来 A 到 B 只要 10ms,经过总机变成 100ms |
| 成本高 | 10 万人同时打电话,总机带宽费用吓人 |
| 隐私差 | 总机理论上可以听到所有通话内容 |
如果不想经过总机呢?
有一种设备叫对讲机:
yaml
你 ════════════ 朋友
按住按钮说话
松开按钮听对方说
对讲机不需要任何中间设备,两个人可以直接通话。
那如果把这个概念搬到互联网上呢?
P2P:互联网上的"对讲机"
什么是 P2P?
P2P(Peer to Peer)= 点对点直连。
简单说,就是两个人直接交换数据,不经过中间服务器:
yaml
传统模式:
用户A ──> 服务器 <── 用户B
P2P 模式:
用户A ═══════════════ 用户B
就像两个人用两根竹竿直接连起来------敲敲竹竿,对方就能听到。
P2P 的优势
| 优势 | 说明 |
|---|---|
| 延迟低 | 数据直连,少绕路 |
| 成本低 | 服务器只需要交换"暗号",不传数据 |
| 隐私好 | 服务器只知道"你们在通话",不知道内容 |
WebRTC:浏览器里的 P2P 神器
什么是 WebRTC?
WebRTC(Web Real-Time Communication)= 网页实时通信。
它是浏览器内置的 P2P 通信 API ,让网页可以直接进行音视频通话、数据传输,不需要安装任何插件。
yaml
WebRTC 能做的事:
┌───────────────────────────────┐
│ 📞 视频通话 - 像微信视频一样 │
│ 📁 文件传输 - P2P 传大文件 │
│ 🎮 游戏数据 - 实时对战游戏 │
│ 📝 实时协作 - 白板、文档协作 │
└───────────────────────────────┘
WebRTC 核心 API
WebRTC 有三个核心 API:
| API | 作用 | 举个例子 |
|---|---|---|
| MediaStream | 采集摄像头/麦克风的音视频 | getUserMedia({video: true}) 获取视频流 |
| RTCPeerConnection | 建立和管理 P2P 连接 | 创建连接、交换 Offer/Answer、处理 ICE 候选 |
| DataChannel | 建立双向数据通道 | 传文件、传游戏操作、聊天消息 |
举个好理解的例子:
三个人要视频通话,就像这样分工:
yaml
┌──────────────────────────────────┐
│ MediaStream = 摄像师 │
│ (负责采集画面和声音) │
│ │
│ RTCPeerConnection = 总调度 │
│ (负责交换信息、建立连接、传输数据) │
│ │
│ DataChannel = 快递员 │
│ (负责传递文件、游戏数据等额外信息) │
└──────────────────────────────────┘
兼容性
| 浏览器 | 支持情况 |
|---|---|
| Chrome | ✅ 56+ |
| Firefox | ✅ 44+ |
| Safari | ✅ 11+ |
| Edge | ✅ 79+ |
💡 桌面端兼容性很好,移动端 Safari 支持较弱,实际项目建议做降级处理。
P2P 的难点:NAT 是什么?
故事:为什么找不到你?
假设你想和朋友 P2P 通话,你知道朋友的电脑 IP 是 192.168.1.100。你直接往这个地址发数据,能发到吗?
发不到。
因为 192.168.1.100 是内网 IP,只有在你家路由器内部才有效。就像:
yaml
🏢 大楼前台 = NAT(网络地址转换)
你的房间号:888(内网 IP:192.168.1.100)
前台改成了:大厅 555(公网 IP:118.123.45.67)
外面的人只知道大厅 555,怎么知道你在 888 房间?
NAT 就像一堵墙,挡住了外面的人直接找到你。
NAT 的四种类型
NAT 有四种"严格程度",就像不同的前台规则:
| NAT 类型 | 规则有多严 | 打个比方 |
|---|---|---|
| 全圆锥型 | 最松 | 任何人都能通过前台找到你 |
| 受限圆锥型 | 较严 | 只有你联系过的人才能通过前台找到你 |
| 端口受限圆锥型 | 更严 | 只有你联系过的具体端口才能找到你 |
| 对称型 | 最严 | 每次联系不同的人,前台都给你分配不同的端口 |
对称型 NAT 最难穿透,因为 STUN 探测出来的地址,换个人可能就用不了了。
解决方案:STUN / TURN / ICE
STUN:探测自己在外的"长相"
STUN = NAT 会话遍历工具。
它的作用很简单:告诉你,你在外面看起来是什么地址。
就像你问前台:"我在外面看起来是多少号?"
yaml
你 ────> STUN 服务器:"我在外面看起来是多少?"
│
↓
"你在外网是 118.123.45.67:55555"
STUN 是怎么工作的?
- 你给 STUN 服务器发一个请求
- STUN 服务器看看这个请求是从哪个公网地址来的
- STUN 服务器告诉你:"你在外面是 XXX.XXX.XXX.XXX:YYYY"
- 你把这个地址告诉朋友,朋友就能直接找到你了
STUN 能解决什么问题?
| 场景 | 能打通吗? |
|---|---|
| 双方都在公网 | ✅ 可以 |
| 一方在内网,一方在公网 | ✅ 可以 |
| 双方都在不同内网 | ❌ 不一定 |
TURN:最后的"中转站"
TURN = 通过中继绕过 NAT。
当 STUN 穿透失败时,TURN 服务器充当中转站:
yaml
用户A ────> TURN 服务器 <───> 用户B
数据中转
TURN 是怎么工作的?
- 双方都连接 TURN 服务器
- 发给对方的数据,先发到 TURN 服务器
- TURN 服务器再转发给对方
⚠️ TURN 只是保底方案,所有数据都要经过它,会产生带宽成本------就像打电话都要经过总机一样。
ICE:自动选择最优方案
ICE = 交互式连接建立。
WebRTC 会同时尝试多种连接方式,自动选择最优的:
| 优先级 | 连接方式 | 什么时候用 |
|---|---|---|
| 1 | 直连 | 双方都能直接找到对方(都在公网) |
| 2 | STUN 穿透 | 一方在内网,但 NAT 不太严格 |
| 3 | TURN 中转 | 双方都在严格 NAT 后面,只能中转 |
ICE 的工作流程:
yaml
ICE 尝试连接:
┌───────────────────────────────┐
│ 1. 收集候选地址 │
│ - 本地 IP │
│ - STUN 探测出的公网 IP │
│ - TURN 服务器分配的地址 │
│ │
│ 2. 对所有候选配对进行连通性检查 │
│ │
│ 3. 按优先级排序,选最快的 │
└───────────────────────────────┘
简单说:能直连就直连,不能直连就穿透,穿不透就中转,WebRTC 自动搞定一切。
信令机制:通话前的"暗号交换"
好,现在假设 A 和 B 都知道对方的公网地址了。
可以直接打电话了吗?
还不行。
在真正通话之前,他们还需要交换一些"暗号"------比如用什么编码、怎么加密等。这个交换"暗号"的过程 ,就叫做信令(Signaling)。
Offer 和 Answer 是什么?
Offer 和 Answer 里装的是 SDP(Session Description Protocol,会话描述协议)。
SDP 就是一份"自我介绍清单":
yaml
SDP 包含的内容:
├── 我支持什么音视频编码?(H.264、VP8、VP9、OPUS...)
├── 我的音视频轨道信息(有几路视频、几路音频)
├── 我的网络地址候选(IP:端口)
├── 带宽限制(最多能用多少带宽)
└── 安全加密方式(用什么加密算法)
- Offer:发起方的自我介绍清单
- Answer:接收方的回应清单,表示"我能接受这些格式,这是我的信息"
信令交换流程
yaml
信令交换流程:
┌─────────┐ ┌─────────┐
│ 用户A │ │ 用户B │
└────┬────┘ └────┬────┘
│ 1. A 创建 Offer(SDP) │
│ ───────────────────────────────>
│ │
│ 2. B 收到 Offer,返回 Answer │
│ <───────────────────────────────
│ │
│ 3. 双方交换 ICE 候选地址 │
│ <============================= >
│ │
│ 4. P2P 连接建立成功!🎉 │
💡 WebRTC 没有定义信令服务器------你可以用 WebSocket、HTTP、甚至 Email,什么方式都行。因为信令只是"暗号",不需要特殊协议。
核心代码:最小示例
WebRTC 代码比较复杂,这里展示最核心的部分:
1. 创建连接
javascript
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
这行代码做了三件事:
- 创建了一个 P2P 连接
- 配置了 STUN 服务器(用来探测公网地址)
- 返回一个连接对象,后面所有的操作都围绕这个对象
2. 交换 Offer/Answer
javascript
// A:创建 Offer
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
sendToPeer(offer); // 发送 Offer 给 B
// B:收到 Offer,设置远程描述,返回 Answer
await pc.setRemoteDescription(offer);
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
sendToPeer(answer); // 发送 Answer 给 A
这里的关键是:
createOffer()/createAnswer()生成 SDPsetLocalDescription()设置本地描述(自己的信息)setRemoteDescription()设置远程描述(对方的信息)
3. 处理 ICE 候选
javascript
// 收到对方的候选地址,加入连接
await pc.addIceCandidate(candidate);
ICE 候选就是"候选的网络地址",可能来自 STUN 探测,也可能来自 TURN 分配。
💡 完整的通话还需要获取麦克风/摄像头、处理对方的音视频轨道等,但原理就是这样:交换 Offer/Answer → 交换 ICE 候选 → 连接建立。
DataChannel:不仅仅是通话
WebRTC 不只能传输音视频,还能传输任意数据。
DataChannel 和 WebSocket 的区别
| 特性 | DataChannel | WebSocket |
|---|---|---|
| 底层协议 | UDP | TCP |
| 延迟 | 更低 | 较高 |
| 可靠性 | 可配置(可靠/不可靠) | 可靠 |
| 适用场景 | 游戏实时操作 | 聊天、推送 |
举个例子:
- 打实时游戏:操控延迟要求极高,用 DataChannel(UDP)
- 聊天消息:不能丢消息,用 WebSocket(TCP)
DataChannel 能做什么
| 场景 | 说明 |
|---|---|
| 游戏数据传输 | 操控指令、位置同步 |
| 文件共享 | P2P 传大文件,不限速 |
| 实时协作 | 白板、文档协作 |
javascript
// 创建数据通道
const dc = pc.createDataChannel('myData');
// 发送消息
dc.send('Hello, P2P!');
// 接收消息
dc.onmessage = (event) => console.log('收到:', event.data);
总结
WebRTC 连接流程
yaml
完整流程:
┌──────────────────────────────────────┐
│ 1. 双方交换公网地址(STUN 探测) │
│ 2. 交换 Offer/Answer(SDP 信令) │
│ 3. 交换 ICE 候选 │
│ 4. NAT 穿透建立连接 │
│ 5. 开始 P2P 传输 │
└──────────────────────────────────────┘
为什么 WebRTC 延迟低?
| 原因 | 说明 |
|---|---|
| 直连 | 数据不经过服务器,少跳转 |
| UDP | 不用等"确认收到",更快 |
| 专用协议 | SRTP 比 HTTP 更轻量 |
使用建议
yaml
✅ 适合的场景:
- 视频通话、在线会议
- 游戏、直播连麦
- 文件传输、白板协作
⚠️ 注意:
- 移动端兼容性较差
- 复杂内网可能需要 TURN 中转
- 需要自己实现信令服务器
❌ 不适合:
- 老浏览器(不支持 WebRTC)
- 需要录制存档(数据不过服务器,无法保存)
写在最后
现在你知道了:
- P2P 就是两个人直接通话,不经过服务器
- NAT 是内网设备上网的"前台",会挡住外面的人
- STUN 帮你探测自己在外的"长相"
- TURN 是最后的"中转站"
- ICE 自动选择最优连接方式
- 信令 是通话前的"暗号交换",交换的是 SDP
- WebRTC 不只是通话,还能传文件、做游戏
下次你用微信视频时,可以想想:这么低的延迟,背后就是 P2P 和 WebRTC 在起作用------两个人就像拿着对讲机,直接喊到对方。
📞 _"喂,你听得到吗?" ------ 这可能是人类历史上最早实现 P2P 直连的两次通话之一。