news 2026/7/7 1:08:03

RTCPeerConnection 连接建立:SDP 与 ICE

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 的原理和配置。

我是黒漂技术佬。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/7 1:01:44

2026年茂名老山檀香厂家推荐:这3家品质靠谱值得信赖

老山檀香市场热度持续走高,但产区混淆、人工优化、新料充老料等行业乱象也让不少香友踩过坑。作为一位从业多年的老山檀香源头商家,我走访了茂名当地多家檀香加工厂和批发点,结合真实手串检测数据和用户反馈,筛选出3家品质过硬、口…

作者头像 李华
网站建设 2026/7/7 1:01:23

【知识总结】命令行系统服务端实现原理

命令行系统总框架 命令行系统由客户端-服务器架构由命令行客户端以及命令行服务器组成。 客户端功能: 以可执行文件的形式呈现,用户直接调用,接受用户在屏幕中的键入,并发送用户的命令行信息给服务端, 最后接受服务端的…

作者头像 李华
网站建设 2026/7/7 0:45:25

5步完成老Mac系统升级:OpenCore Legacy Patcher终极指南

5步完成老Mac系统升级:OpenCore Legacy Patcher终极指南 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 还在为你的老旧Mac无法升级到最新macOS而…

作者头像 李华
网站建设 2026/7/7 0:40:13

SecHex-Spoofy深度解析:专业级HWID修改工具实战指南

SecHex-Spoofy深度解析:专业级HWID修改工具实战指南 【免费下载链接】SecHex-Spoofy C# HWID Changer 🔑︎ Disk, Guid, Mac, Gpu, Pc-Name, Win-ID, EFI, SMBIOS Spoofing [Usermode] 项目地址: https://gitcode.com/gh_mirrors/se/SecHex-Spoofy …

作者头像 李华
网站建设 2026/7/7 0:39:31

CSS 选择器性能:别让样式匹配变成隐形税

CSS 选择器性能:别让样式匹配变成隐形税 一、选择器问题不会总是出现在小 Demo 里 CSS 选择器性能在小项目里很难显眼。页面节点少,怎么写都差不多。到了复杂后台、低代码页面或长列表,过深选择器、通配符、后代选择器叠起来,样式…

作者头像 李华
网站建设 2026/7/7 0:27:10

每日 AI 研究简报 · 2026-07-06

(本文借助 AI 大模型及工具辅助整理) 一句话总结:今日 AI 主线在「能力」与「可控」之间加速校准——持久状态攻击面、多智能体红队工具走热,Anthropic 模型解禁与阿里把 Agent 成本压降 99% 同步发生,企业开始用更便…

作者头像 李华