不经过服务器,两个人怎么直接通话?

你有没有想过:微信视频、腾讯会议为什么延迟那么低?两个人在不同的城市,怎么才能不经过服务器直接"喊到"对方?

今天,用对讲机的故事,来讲讲浏览器里的 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 是怎么工作的?

  1. 你给 STUN 服务器发一个请求
  2. STUN 服务器看看这个请求是从哪个公网地址来的
  3. STUN 服务器告诉你:"你在外面是 XXX.XXX.XXX.XXX:YYYY"
  4. 你把这个地址告诉朋友,朋友就能直接找到你了

STUN 能解决什么问题?

场景 能打通吗?
双方都在公网 ✅ 可以
一方在内网,一方在公网 ✅ 可以
双方都在不同内网 ❌ 不一定

TURN:最后的"中转站"

TURN = 通过中继绕过 NAT。

当 STUN 穿透失败时,TURN 服务器充当中转站

yaml 复制代码
用户A  ────>  TURN 服务器  <───>  用户B
              数据中转

TURN 是怎么工作的?

  1. 双方都连接 TURN 服务器
  2. 发给对方的数据,先发到 TURN 服务器
  3. 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' }]
});

这行代码做了三件事:

  1. 创建了一个 P2P 连接
  2. 配置了 STUN 服务器(用来探测公网地址)
  3. 返回一个连接对象,后面所有的操作都围绕这个对象

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() 生成 SDP
  • setLocalDescription() 设置本地描述(自己的信息)
  • 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 直连的两次通话之一。

相关推荐
神探小白牙2 小时前
3D饼图,带背景图和自定义图例(threejs)
开发语言·前端·javascript·3d·vue
IT_陈寒2 小时前
SpringBoot自动配置的坑差点没把我埋了
前端·人工智能·后端
光影少年2 小时前
高级前端需要学习那些东西?
前端·人工智能·学习·aigc·ai编程
jiayong232 小时前
第 41 课:任务详情抽屉里的快速筛选联动
开发语言·前端·javascript·vue.js·学习
momo(激进版)2 小时前
常用的skills安装记录
前端
zimoyin2 小时前
Stoplight Elements WebComponents 原生 WEB 组件化技术生成 Swagger优美界面
前端
天若有情6732 小时前
【开源推荐】form-validator-cn 轻量级中文表单校验库 | TS 零依赖、极简开箱即用
前端·npm·开源·node·js·表单校验
shjita2 小时前
maven涉及的配置
java·前端·maven
changshuaihua0012 小时前
useState 状态管理
开发语言·前端·javascript·react.js