RTCPeerConnection 连接建立:SDP 与 ICE
RTCPeerConnection 连接建立SDP 与 ICERTCPeerConnection 是 WebRTC 的核心负责建立点对点连接、传输音视频。这篇讲连接建立的完整流程SDP 协商、ICE 候选、Offer/Answer 交换一步步把连接建起来。大家好我是黒漂技术佬。前面讲了音视频采集有了本地媒体流接下来要传给对方——这就靠 RTCPeerConnection。RTCPeerConnection 是 WebRTC 最核心的 API也是最复杂的。SDP 是什么ICE 是什么Offer 和 Answer 怎么交换为什么需要信令服务器这篇把连接建立的完整流程讲清楚从创建 PeerConnection 到打通 P2P 通道。一、RTCPeerConnection 是什么作用RTCPeerConnection 负责两端之间的连接建立和媒体数据传输媒体协商两边商量用什么编解码器、分辨率、码率网络穿透NAT 打洞找到能连通的网络路径安全传输加密传输音视频数据SRTP质量控制拥塞控制、丢包恢复、自适应码率创建constpcnewRTCPeerConnection({iceServers:[{urls:stun:stun.l.google.com:19302}// STUN 服务器]});参数里配置 ICE 服务器STUN/TURN用来 NAT 穿透。二、SDP 是什么全称Session Description Protocol会话描述协议。是什么一段文本描述媒体会话的所有信息有哪些媒体流视频、音频用什么编解码器VP8、H.264、Opus…媒体的传输地址IP端口ICE 候选填进来加密信息带宽信息长什么样v0 o- 1234567890 2 IN IP4 127.0.0.1 s- t0 0 agroup:BUNDLE audio video maudio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 cIN IP4 0.0.0.0 artpmap:111 opus/48000/2 ... mvideo 9 UDP/TLS/RTP/SAVPF 100 101 107 116 cIN IP4 0.0.0.0 artpmap:100 VP8/90000 ...不用深究每一行知道它是描述媒体配置的文本就行。Offer / Answer 模型连接建立用的是 Offer-Answer 模式发起方A生成 Offer SDP我这边的媒体配置A 把 Offer 发给接收方BB 收到 Offer生成 Answer SDP我这边的配置和你协商好B 把 Answer 发回给 A两边都有了对方的 SDP媒体协商完成三、ICE 是什么全称Interactive Connectivity Establishment交互式连接建立。作用解决 NAT 穿透问题找到两端之间能连通的网络路径。ICE CandidateICE 候选每个候选是一个可能的连接地址candidate:1234567890 1 udp 2122260223 192.168.1.100 55555 typ host包含IP、端口、协议、类型host/srflx/relay、优先级。三种候选类型类型说明优先级host本机内网地址最高延迟最低srflx服务器反射地址STUN 拿到的公网地址中relayTURN 中继地址最低延迟高、走服务器ICE 会按优先级从高到低尝试找到第一个能连通的就用。ICE 收集过程创建 PeerConnection 后开始收集候选先收集 host 候选本机所有网卡的地址然后用 STUN 服务器获取 srflx 候选公网映射地址最后收集 relay 候选TURN 中继地址收集到一个就触发一次onicecandidate事件通过信令发给对方。四、完整的连接建立流程前提A 和 B 都连上了信令服务器可以互相发消息步骤 1创建 PeerConnection添加媒体轨// A 和 B 各自创建constpcnewRTCPeerConnection({iceServers:[...]});// 把本地媒体流加进去stream.getTracks().forEach(track{pc.addTrack(track,stream);});步骤 2A 创建 Offer// A 端发起方constofferawaitpc.createOffer();awaitpc.setLocalDescription(offer);// 通过信令把 offer 发给 Bsignaling.send({type:offer,sdp:offer.sdp});setLocalDescription 后ICE 开始收集候选。步骤 3B 收到 Offer创建 Answer// B 端接收方asyncfunctiononOffer(offer){awaitpc.setRemoteDescription(newRTCSessionDescription(offer));constanswerawaitpc.createAnswer();awaitpc.setLocalDescription(answer);// 通过信令把 answer 发回给 Asignaling.send({type:answer,sdp:answer.sdp});}步骤 4A 收到 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));}步骤 6ICE 连接成功两边候选交换完开始配对尝试连接。成功后触发oniceconnectionstatechangepc.oniceconnectionstatechange(){console.log(ICE 状态:,pc.iceConnectionState);// checking → connected/completed};状态变成connected或completed就说明连接通了。步骤 7接收远端媒体流pc.ontrack(event){constremoteStreamevent.streams[0];remoteVideo.srcObjectremoteStream;};对方的媒体流过来了显示到页面上。五、状态机ICE 连接状态状态说明new初始状态checking正在检查候选配对connected找到可用连接可能还在找更好的completed检查完了找到了最好的连接failed所有候选都试过了连不上disconnected连接断了可能暂时的closed关闭了信令状态状态说明stable稳定状态没有待处理的 offerhave-local-offer本地设置了 offer等对方 answerhave-remote-offer收到了对方的 offer要生成 answerhave-local-pranswer本地发了暂定 answerhave-remote-pranswer收到了对方的暂定 answerclosed关闭状态机很重要顺序错了会报错。比如还没收到 offer 就 createAnswer 会失败。六、信令服务器为什么需要信令服务器WebRTC 标准只定义了媒体传输不管怎么交换联系方式。SDP 和 ICE 候选怎么交换自己想办法。信令要做什么房间管理用户加入、离开房间转发消息Offer、Answer、ICE Candidate成员管理谁在房间里怎么实现随便什么都可以只要能双向通信WebSocket最常用Socket.IOHTTP 长轮询甚至用信鸽开玩笑的最简单的是 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}七、最简一对一通话代码框架前端发起方 AconstpcnewRTCPeerConnection({iceServers:[...]});constlocalStreamawaitgetUserMedia({video:true,audio:true});// 添加本地轨道localStream.getTracks().forEach(tpc.addTrack(t,localStream));localVideo.srcObjectlocalStream;// 收到远端流pc.ontrackeremoteVideo.srcObjecte.streams[0];// ICE 候选pc.onicecandidatee{if(e.candidate)signaling.send({type:candidate,candidate:e.candidate});};// 发起呼叫asyncfunctioncall(){constofferawaitpc.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));});前端接收方 BconstpcnewRTCPeerConnection({iceServers:[...]});constlocalStreamawaitgetUserMedia({video:true,audio:true});localStream.getTracks().forEach(tpc.addTrack(t,localStream));localVideo.srcObjectlocalStream;pc.ontrackeremoteVideo.srcObjecte.streams[0];pc.onicecandidatee{if(e.candidate)signaling.send({type:candidate,candidate:e.candidate});};// 收到 Offersignaling.on(offer,async(data){awaitpc.setRemoteDescription(newRTCSessionDescription(data));constanswerawaitpc.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. 轨道加了吗有没有调用 addTrack2. SDP 交换成功了吗两边都 setRemoteDescription 了吗3. ICE 连接成功了吗iceConnectionState 是 connected 吗4. video 标签有没有 autoplay移动端需要用户手势触发播放。九、常见坑坑 1setLocalDescription 之后才开始收 ICE 候选没设本地描述前不会收集候选。顺序别搞反。坑 2信令顺序错了Offer/Answer 有严格顺序状态机不对会报错。比如收到两个 offer 没处理好。坑 3ICE 候选要及时转发收集到一个就发一个Trickle ICE别等全部收集完再发慢很多。坑 4没有 STUN 服务器内网可以通外网不行同局域网内不用 STUN 也能连跨网就不行了。生产环境一定要配 STUN/TURN。坑 5TURN 服务器带宽成本高走 TURN 中继的话所有流量都过服务器人多了带宽成本很高。尽量让 P2P 打通。坑 6移动端后台连接断了App 切后台WebSocket 和 WebRTC 连接可能断要处理重连。十、本篇小结RTCPeerConnection 是 WebRTC 核心负责连接建立和媒体传输SDP会话描述协商媒体配置编解码、加密等ICENAT 穿透找连通路径有 host/srflx/relay 三种候选连接流程创建 PC → 加轨道 → Offer → Answer → 交换 ICE → 连接成功信令服务器交换 SDP 和 ICE 候选自己实现WebSocket 最常用状态机很重要Offer/Answer 顺序不能乱连接不上先查 ICE 状态、候选交换、STUN/TURN 配置下一篇深入讲NAT 穿透STUN、TURN、ICE 的原理和配置。我是黒漂技术佬。