当前位置: 首页 > news >正文

RTCPeerConnection 连接建立:SDP 与 ICE

RTCPeerConnection 连接建立:SDP 与 ICE

RTCPeerConnection 是 WebRTC 的核心,负责建立点对点连接、传输音视频。这篇讲连接建立的完整流程:SDP 协商、ICE 候选、Offer/Answer 交换,一步步把连接建起来。

大家好,我是黒漂技术佬。

前面讲了音视频采集,有了本地媒体流,接下来要传给对方——这就靠 RTCPeerConnection。

RTCPeerConnection 是 WebRTC 最核心的 API,也是最复杂的。SDP 是什么?ICE 是什么?Offer 和 Answer 怎么交换?为什么需要信令服务器?

这篇把连接建立的完整流程讲清楚,从创建 PeerConnection 到打通 P2P 通道。


一、RTCPeerConnection 是什么?

作用

RTCPeerConnection 负责两端之间的连接建立和媒体数据传输:

  1. 媒体协商:两边商量用什么编解码器、分辨率、码率
  2. 网络穿透:NAT 打洞,找到能连通的网络路径
  3. 安全传输:加密传输音视频数据(SRTP)
  4. 质量控制:拥塞控制、丢包恢复、自适应码率

创建

constpc=newRTCPeerConnection({iceServers:[{urls:'stun:stun.l.google.com:19302'}// STUN 服务器]});

参数里配置 ICE 服务器(STUN/TURN),用来 NAT 穿透。


二、SDP 是什么?

全称

Session Description Protocol,会话描述协议。

是什么

一段文本,描述媒体会话的所有信息:

  • 有哪些媒体流(视频、音频)
  • 用什么编解码器(VP8、H.264、Opus…)
  • 媒体的传输地址(IP+端口,ICE 候选填进来)
  • 加密信息
  • 带宽信息

长什么样

v=0 o=- 1234567890 2 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE audio video m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 c=IN IP4 0.0.0.0 a=rtpmap:111 opus/48000/2 ... m=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116 c=IN IP4 0.0.0.0 a=rtpmap:100 VP8/90000 ...

不用深究每一行,知道它是描述媒体配置的文本就行。

Offer / Answer 模型

连接建立用的是 Offer-Answer 模式:

  1. 发起方(A)生成 Offer SDP(我这边的媒体配置)
  2. A 把 Offer 发给接收方(B)
  3. B 收到 Offer,生成 Answer SDP(我这边的配置,和你协商好)
  4. B 把 Answer 发回给 A
  5. 两边都有了对方的 SDP,媒体协商完成

三、ICE 是什么?

全称

Interactive Connectivity Establishment,交互式连接建立。

作用

解决 NAT 穿透问题,找到两端之间能连通的网络路径。

ICE Candidate(ICE 候选)

每个候选是一个可能的连接地址:

candidate:1234567890 1 udp 2122260223 192.168.1.100 55555 typ host

包含:IP、端口、协议、类型(host/srflx/relay)、优先级。

三种候选类型

类型说明优先级
host本机内网地址最高(延迟最低)
srflx服务器反射地址(STUN 拿到的公网地址)
relayTURN 中继地址最低(延迟高、走服务器)

ICE 会按优先级从高到低尝试,找到第一个能连通的就用。

ICE 收集过程

创建 PeerConnection 后,开始收集候选:

  1. 先收集 host 候选(本机所有网卡的地址)
  2. 然后用 STUN 服务器获取 srflx 候选(公网映射地址)
  3. 最后收集 relay 候选(TURN 中继地址)

收集到一个就触发一次onicecandidate事件,通过信令发给对方。


四、完整的连接建立流程

前提

  • A 和 B 都连上了信令服务器
  • 可以互相发消息

步骤 1:创建 PeerConnection,添加媒体轨

// A 和 B 各自创建constpc=newRTCPeerConnection({iceServers:[...]});// 把本地媒体流加进去stream.getTracks().forEach(track=>{pc.addTrack(track,stream);});

步骤 2:A 创建 Offer

// A 端:发起方constoffer=awaitpc.createOffer();awaitpc.setLocalDescription(offer);// 通过信令把 offer 发给 Bsignaling.send({type:'offer',sdp:offer.sdp});

setLocalDescription 后,ICE 开始收集候选。

步骤 3:B 收到 Offer,创建 Answer

// B 端:接收方asyncfunctiononOffer(offer){awaitpc.setRemoteDescription(newRTCSessionDescription(offer));constanswer=awaitpc.createAnswer();awaitpc.setLocalDescription(answer);// 通过信令把 answer 发回给 Asignaling.send({type:'answer',sdp:answer.sdp});}

步骤 4:A 收到 Answer

// A 端asyncfunctiononAnswer(answer){awaitpc.setRemoteDescription(newRTCSessionDescription(answer));}

到这里,SDP 交换完成,媒体协商好了。

步骤 5:交换 ICE 候选

两边各自收集候选,收集到一个就发给对方:

// A 和 B 都要处理pc.onicecandidate=(event)=>{if(event.candidate){// 发给对方signaling.send({type:'ice-candidate',candidate:event.candidate});}};

收到对方的候选,加进去:

functiononIceCandidate(candidate){pc.addIceCandidate(newRTCIceCandidate(candidate));}

步骤 6:ICE 连接成功

两边候选交换完,开始配对尝试连接。成功后触发oniceconnectionstatechange

pc.oniceconnectionstatechange=()=>{console.log('ICE 状态:',pc.iceConnectionState);// checking → connected/completed};

状态变成connectedcompleted就说明连接通了。

步骤 7:接收远端媒体流

pc.ontrack=(event)=>{constremoteStream=event.streams[0];remoteVideo.srcObject=remoteStream;};

对方的媒体流过来了,显示到页面上。


五、状态机

ICE 连接状态

状态说明
new初始状态
checking正在检查候选配对
connected找到可用连接(可能还在找更好的)
completed检查完了,找到了最好的连接
failed所有候选都试过了,连不上
disconnected连接断了(可能暂时的)
closed关闭了

信令状态

状态说明
stable稳定状态,没有待处理的 offer
have-local-offer本地设置了 offer,等对方 answer
have-remote-offer收到了对方的 offer,要生成 answer
have-local-pranswer本地发了暂定 answer
have-remote-pranswer收到了对方的暂定 answer
closed关闭

状态机很重要,顺序错了会报错。比如还没收到 offer 就 createAnswer 会失败。


六、信令服务器

为什么需要信令服务器?

WebRTC 标准只定义了媒体传输,不管怎么交换联系方式。SDP 和 ICE 候选怎么交换,自己想办法。

信令要做什么

  1. 房间管理:用户加入、离开房间
  2. 转发消息:Offer、Answer、ICE Candidate
  3. 成员管理:谁在房间里

怎么实现

随便什么都可以,只要能双向通信:

  • WebSocket(最常用)
  • Socket.IO
  • HTTP 长轮询
  • 甚至用信鸽(开玩笑的)

最简单的是 Node.js + Socket.IO,几十行代码就能搭一个。

信令消息类型

// 加入房间{type:'join',roomId:'123',userId:'userA'}// 转发 Offer{type:'offer',from:'userA',to:'userB',sdp:'...'}// 转发 Answer{type:'answer',from:'userB',to:'userA',sdp:'...'}// 转发 ICE 候选{type:'ice-candidate',from:'userA',to:'userB',candidate:{...}}// 离开{type:'leave',userId:'userA'}

七、最简一对一通话代码框架

前端(发起方 A)

constpc=newRTCPeerConnection({iceServers:[...]});constlocalStream=awaitgetUserMedia({video:true,audio:true});// 添加本地轨道localStream.getTracks().forEach(t=>pc.addTrack(t,localStream));localVideo.srcObject=localStream;// 收到远端流pc.ontrack=e=>remoteVideo.srcObject=e.streams[0];// ICE 候选pc.onicecandidate=e=>{if(e.candidate)signaling.send({type:'candidate',candidate:e.candidate});};// 发起呼叫asyncfunctioncall(){constoffer=awaitpc.createOffer();awaitpc.setLocalDescription(offer);signaling.send({type:'offer',sdp:offer.sdp});}// 收到 Answersignaling.on('answer',async(data)=>{awaitpc.setRemoteDescription(newRTCSessionDescription(data));});// 收到 ICE 候选signaling.on('candidate',async(candidate)=>{awaitpc.addIceCandidate(newRTCIceCandidate(candidate));});

前端(接收方 B)

constpc=newRTCPeerConnection({iceServers:[...]});constlocalStream=awaitgetUserMedia({video:true,audio:true});localStream.getTracks().forEach(t=>pc.addTrack(t,localStream));localVideo.srcObject=localStream;pc.ontrack=e=>remoteVideo.srcObject=e.streams[0];pc.onicecandidate=e=>{if(e.candidate)signaling.send({type:'candidate',candidate:e.candidate});};// 收到 Offersignaling.on('offer',async(data)=>{awaitpc.setRemoteDescription(newRTCSessionDescription(data));constanswer=awaitpc.createAnswer();awaitpc.setLocalDescription(answer);signaling.send({type:'answer',sdp:answer.sdp});});signaling.on('candidate',async(candidate)=>{awaitpc.addIceCandidate(newRTCIceCandidate(candidate));});

八、常见问题排查

连接不上怎么办?

1. 看 ICE 状态
console.log(pc.iceConnectionState);
  • 一直 checking:候选没交换完,或者网络不通
  • failed:所有候选都不行,可能需要 TURN 服务器
2. 检查 ICE 候选有没有交换
  • 两边都收到对方的 candidate 了吗?
  • 信令服务器有没有转发成功?
3. 检查 STUN/TURN 服务器配置
  • 地址对不对?
  • 端口有没有被防火墙挡?
  • TURN 的用户名密码对不对?
4. 用 trickle-ice 工具测试

网上有在线的 WebRTC 测试工具,可以测 STUN/TURN 能不能用。

视频出不来怎么办?

1. 轨道加了吗?

有没有调用 addTrack?

2. SDP 交换成功了吗?

两边都 setRemoteDescription 了吗?

3. ICE 连接成功了吗?

iceConnectionState 是 connected 吗?

4. video 标签有没有 autoplay?

移动端需要用户手势触发播放。


九、常见坑

坑 1:setLocalDescription 之后才开始收 ICE 候选

没设本地描述前不会收集候选。顺序别搞反。

坑 2:信令顺序错了

Offer/Answer 有严格顺序,状态机不对会报错。比如收到两个 offer 没处理好。

坑 3:ICE 候选要及时转发

收集到一个就发一个(Trickle ICE),别等全部收集完再发,慢很多。

坑 4:没有 STUN 服务器内网可以通,外网不行

同局域网内不用 STUN 也能连,跨网就不行了。生产环境一定要配 STUN/TURN。

坑 5:TURN 服务器带宽成本高

走 TURN 中继的话,所有流量都过服务器,人多了带宽成本很高。尽量让 P2P 打通。

坑 6:移动端后台连接断了

App 切后台,WebSocket 和 WebRTC 连接可能断,要处理重连。


十、本篇小结

  • RTCPeerConnection 是 WebRTC 核心,负责连接建立和媒体传输
  • SDP:会话描述,协商媒体配置(编解码、加密等)
  • ICE:NAT 穿透,找连通路径,有 host/srflx/relay 三种候选
  • 连接流程:创建 PC → 加轨道 → Offer → Answer → 交换 ICE → 连接成功
  • 信令服务器:交换 SDP 和 ICE 候选,自己实现,WebSocket 最常用
  • 状态机很重要,Offer/Answer 顺序不能乱
  • 连接不上先查 ICE 状态、候选交换、STUN/TURN 配置

下一篇深入讲NAT 穿透:STUN、TURN、ICE 的原理和配置。

我是黒漂技术佬。

http://www.jsqmd.com/news/1138180/

相关文章:

  • 线程池参数调优:corePoolSize 怎么设置
  • VCC/VDD/VSS 电源符号深度解析:3 种命名体系对比与 PCB 布局 4 要点
  • 英雄联盟自动化助手:从繁琐设置到智能游戏体验的革命
  • 15个实用的API文档生成工具,自动化脚本
  • 第一次编写Java程序/26.7.6
  • Kazumi:基于自定义规则的跨平台动漫聚合播放器技术解析与架构设计
  • 系统规划与管理师-应用系统规划核心知识点:过程、方法与软件工厂
  • 猫抓Cat-Catch终极指南:浏览器资源嗅探扩展的完整技术解析与实战教程
  • 宠物零食配方工艺:从原料工程到加工技术落地拆解
  • WarcraftHelper:魔兽争霸3现代化改造终极指南
  • LM2596S-12/NOPB 降压板实战:立创EDA与AD18双版本PCB布局对比分析
  • IGBT/SiC 功率模块热阻计算实战:从 Rth(j-c) 到散热器选型的 5 步法
  • Bilibili视频下载器:5步解锁4K大会员内容的完整指南
  • PADS VX2.8 BGA扇出规则对比:十字通道、米字通道与四分之一圆周的3种策略
  • PWM 频率 1kHz vs 10kHz 对比:Maxon 电机实测 3 大性能差异
  • 基于STM32单片机烟雾温度防盗报警 物联网云平台 火灾检测系统31(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • 从零到一搞懂 AI Agent:原理、工作流程、LangChain Tool Use 与实战
  • 新冠疫苗 RSA Challenge:进制构造素数漏洞拆解
  • 终极指南:使用res-downloader轻松下载全网视频音频资源,3分钟快速上手!
  • 基于51单片机的智能路灯控制系统 人体感应 灯光控制 嵌入式定制231(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • 5分钟掌握Layui-Admin:构建企业级后台管理系统的终极指南
  • 5分钟让你的Windows焕然一新:终极毛玻璃效果美化工具
  • LibreCAD:为什么这款开源2D CAD软件能成为你的设计利器?
  • Allegro PCB 17.4 实战:5步高效导入DXF结构图与3个常见错误排查
  • 惊!AI 巨头 Anthropic 对中国用户暗藏黑手,国产大模型崛起刻不容缓!
  • 终极Minecraft基岩版启动器:Bedrock Launcher完整指南与实战应用
  • 【OpenHarmony/HarmonyOs 】CheckMe 屏幕检测工具实践:坏点检测、色域检查、触摸测试与全屏沉浸模式
  • 手把手教程:用AI工具做个人IP定位诊断的完整流程
  • 模块化威胁情报分析框架AIL:从数据采集到检测的实战架构
  • Moneta Markets亿汇:把外汇投教内容建设做扎实,注重效率的使用者更容易感受到的要点