5、RTCPeerConnection入门:从零搭建P2P连接
好,咱们今天聊点硬核的。RTCPeerConnection,简称PC,是WebRTC里最核心的对象。说白了,它就是负责建立和维护两个浏览器之间点对点通信的那个“管道工”。
我个人习惯把RTCPeerConnection理解成一个“黑盒子”——你往里扔音视频流,它负责找到对方、协商参数、穿透NAT,最后把数据送过去。你不需要知道它内部怎么拧螺丝,但得知道怎么用它。
5.1 创建RTCPeerConnection对象
创建PC对象其实很简单,一行代码的事。但这里有个坑——必须传配置,尤其是ICE服务器列表。
const config = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{
urls: 'turn:your-turn-server.com:3478',
username: 'your-username',
credential: 'your-credential'
}
]
};
const pc = new RTCPeerConnection(config);
嗯,这里要注意:iceServers 是数组,可以配多个STUN和TURN服务器。我刚开始做项目时,只配了一个STUN,结果在复杂网络环境下死活连不上。后来才明白,STUN不是万能的,遇到对称NAT它就没辙了。
5.2 ICE框架:找路的过程
ICE(Interactive Connectivity Establishment)是WebRTC的“导航系统”。它的任务就是:找到一条能让两端通信的路。
ICE会尝试三种路径:
- 本地候选(Host Candidate):同一局域网内,直接连。最快,但只在局域网有效。
- 反射候选(Server Reflexive Candidate):通过STUN服务器获取公网IP和端口。能穿透大部分NAT。
- 中继候选(Relay Candidate):通过TURN服务器中转数据。最慢,但最可靠。
ICE会收集所有候选地址,然后排序、测试。优先级是:Host > Srflx > Relay。说白了,能直连绝不绕路。
5.3 STUN/TURN服务器配置
STUN和TURN,名字像兄弟,作用完全不同。
| 特性 | STUN | TURN |
|---|---|---|
| 作用 | 获取公网IP和端口 | 中转音视频数据 |
| 带宽消耗 | 极低(仅信令) | 高(转发媒体流) |
| 适用场景 | 大多数NAT环境 | 对称NAT或防火墙限制 |
| 成本 | 免费或极低 | 较高(带宽费用) |
我曾经在一个项目中只配了STUN,结果用户反馈“有时候能连,有时候连不上”。排查后发现,那些连不上的用户都在企业内网,用的是对称NAT。STUN拿到的地址根本没用,必须上TURN。
5.4 Offer/Answer模型与SDP协议
Offer/Answer是WebRTC的“握手协议”。流程很简单:
- A方创建Offer(包含A支持的音视频编码、分辨率、ICE候选等)
- A方通过信令通道把Offer发给B
- B方收到Offer,创建Answer(包含B支持的参数)
- B方把Answer发回给A
这个过程中交换的“参数包”就是SDP(Session Description Protocol)。SDP本质上是一段文本,描述了媒体能力、网络信息等。
// A方:创建Offer
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
// 通过信令发送offer给B
// B方:接收Offer并创建Answer
await pc.setRemoteDescription(offer);
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
// 通过信令发送answer给A
你想想看,这个过程像不像两个人打电话?A说“我能说中文和英文,你那边呢?”B说“我只会中文,那就用中文吧。”——这就是Offer/Answer。
m=(媒体行)和 a=(属性行)。调试时打印SDP出来看看,能帮你快速定位问题。
5.5 建立P2P连接的基本流程
好了,我们把所有知识点串起来。一个完整的P2P连接流程是这样的:
- 创建PC对象:配好STUN/TURN服务器
- 添加媒体流:通过
addTrack()或addTransceiver()把本地音视频加进去 - 创建Offer:调用
createOffer(),然后setLocalDescription() - 信令交换:通过WebSocket或其他方式把Offer发给远端
- 远端处理:远端调用
setRemoteDescription(),然后创建Answer - 交换Answer:把Answer发回给发起方
- ICE候选交换:两端通过
onicecandidate事件收集并交换候选地址 - 连接建立:ICE找到可用路径,触发
oniceconnectionstatechange事件,状态变为connected
嗯,这里有个关键点:ICE候选的交换是异步的。不要等所有候选都收集完再发,而是每收集到一个就立刻发出去。这样可以加速连接建立。
这个图把整个流程串起来了。你仔细看,Offer/Answer是单向的(A→B→A),而ICE候选是双向的(两边同时发)。
oniceconnectionstatechange 事件,打印状态变化。如果卡在 checking 状态不动,大概率是STUN/TURN配置有问题。
好了,RTCPeerConnection的基本用法就这些。说白了就是三步:创建对象、交换SDP、交换ICE候选。剩下的就是监听事件、处理数据流了。
我个人建议你动手写个demo,哪怕只是两个页面之间互相ping一下。只有亲手跑通一次,你才能真正理解这些概念。