求助:使用react-native-webrtc连接两台安卓设备的SDP交互问题
React Native WebRTC 安卓设备连接:解决Offer传递、Answer创建及状态错误问题
我来帮你捋捋你遇到的这些问题——其实核心是WebRTC的连接流程有严格的顺序要求,还有你可能忽略了ICE候选的传递,以及信号通道的正确用法。
为什么会出现"can't use createAnswer() from this state"错误?
你的代码里createAnswer的调用逻辑完全错了:
- WebRTC要求必须先把对方的Offer设置为自己的
remoteDescription,才能调用createAnswer,你直接给createAnswer传sdp参数是无效的,这个方法不需要传参数,它依赖已经设置好的remoteDescription。 - 另外,发起方创建Offer后,要先完成
setLocalDescription,再把完整的Offer对象传给对方,不能只传sdp字符串。
手动复制SDP测试的正确方式
手动复制粘贴不是长久之计,但用来测试的话,一定要传递完整的SDP JSON对象,比如设备A输出的Offer是:
{"type":"offer","sdp":"v=0\r\no=-..."}
你得把整个JSON复制到设备B,而不是只复制sdp字段里的内容——如果type丢失,设备B根本识别不了这是一个Offer,自然无法创建Answer。
信号传递:你必须用Socket(或实时通信工具)
手动复制只能临时测试,实际开发必须用实时通信通道(比如WebSocket、Socket.io、MQTT)来传递两类数据:
- SDP Offer/Answer:双方的会话描述,用来协商媒体参数
- ICE候选:设备的网络地址信息,用来穿透NAT建立直接连接
没有这个通道,你根本没法实时传递这些关键数据,连接自然建立不起来。
修正后的完整代码示例
设备A(发起方)
import { RTCPeerConnection } from 'react-native-webrtc'; // 必须配置ICE服务器,STUN用来获取公网地址,TURN用来中转(可选但推荐) const configuration = { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] }; let pc = new RTCPeerConnection(configuration); // 监听ICE候选,生成后立刻传给对方 pc.onicecandidate = (event) => { if (event.candidate) { // 这里替换成你的Socket发送逻辑,比如:socket.emit('ice-candidate', event.candidate) console.log("A的ICE候选", JSON.stringify(event.candidate)); } }; // 创建Offer的函数 const createOffer = async () => { try { const offer = await pc.createOffer(); // 先设置本地描述,再发送Offer await pc.setLocalDescription(offer); // 把完整的Offer对象发给设备B console.log("A的Offer", JSON.stringify(offer)); } catch (error) { console.error("创建Offer失败", error); } }; // 接收设备B的Answer const handleAnswer = async (answer) => { await pc.setRemoteDescription(new RTCSessionDescription(answer)); }; // 接收设备B的ICE候选 const handleIceCandidate = async (candidate) => { await pc.addIceCandidate(new RTCIceCandidate(candidate)); };
设备B(应答方)
import { RTCPeerConnection } from 'react-native-webrtc'; const configuration = { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] }; let pc = new RTCPeerConnection(configuration); // 监听ICE候选,传给设备A pc.onicecandidate = (event) => { if (event.candidate) { // socket.emit('ice-candidate', event.candidate) console.log("B的ICE候选", JSON.stringify(event.candidate)); } }; // 处理设备A的Offer并创建Answer const handleOffer = async (offer) => { try { // 第一步:把收到的Offer设置为远程描述 await pc.setRemoteDescription(new RTCSessionDescription(offer)); // 第二步:创建Answer const answer = await pc.createAnswer(); // 第三步:设置本地描述,再发送Answer await pc.setLocalDescription(answer); console.log("B的Answer", JSON.stringify(answer)); } catch (error) { console.error("创建Answer失败", error); } }; // 接收设备A的ICE候选 const handleIceCandidate = async (candidate) => { await pc.addIceCandidate(new RTCIceCandidate(candidate)); };
ICE服务器是必须的吗?
绝对是!几乎所有安卓设备都在NAT网络后面,没有STUN服务器的话,设备无法获取自己的公网地址,根本没法和其他设备建立直接连接。如果遇到严格NAT的网络环境(比如某些企业内网),还需要配置TURN服务器作为数据中转。
完整连接流程总结
- 双方初始化RTCPeerConnection并配置ICE服务器
- 发起方创建Offer → 设置localDescription → 把Offer传给应答方
- 应答方接收Offer → 设置remoteDescription → 创建Answer → 设置localDescription → 把Answer传给发起方
- 双方实时传递ICE候选,互相添加对方的候选
- 当ICE连接成功后,PeerConnection的
onconnectionstatechange会触发,状态变为connected,这时候就可以传输媒体数据了
内容的提问来源于stack exchange,提问作者Pivil
相关产品推荐
相关产品推荐

