WebRTC:RTCDataChannel不是“打开”

我跟随了关于WebRTC的整个教程,并实现了一个简单的P2P聊天。 我的信令服务器在localhost:9090上工作。 当我尝试发送消息时,我收到:

RTCDataChannel.readyState is not 'open' 

但是,这个连接似乎已经正确build立:

 Connected Got message {"type":"login","success":true} RTCPeerConnection object was created RTCPeerConnection {localDescription: RTCSessionDescription, remoteDescription: RTCSessionDescription, signalingState: "stable", iceGatheringState: "new", iceConnectionState: "new"…} Channel created Got message {"type":"answer","answer":{"type":"answer","sdp":"v=0\r\no=- 5123156273253761787 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE data\r\na=msid-semantic: WMS\r\nm=application 9 UDP/TLS/RTP/SAVPF 127\r\nc=IN IP4 0.0.0.0\r\nb=AS:30\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:aWnc+x1ot0kpmCj6\r\na=ice-pwd:o8BH8EIsb/FVLBDkUt5Mw6V4\r\na=fingerprint:sha-256 D6:18:83:20:FC:3F:0B:87:8F:FB:D8:5D:D6:33:13:FE:C6:EE:53:3D:18:69:DD:C0:BF:23:35:95:F7:26:4D:F2\r\na=setup:active\r\na=mid:data\r\na=sendrecv\r\na=rtcp-mux\r\na=rtpmap:127 google-data/90000\r\na=ssrc:2024832766 cname:y/zAQto2dzSH04r0\r\na=ssrc:2024832766 msid:myDataChannel myDataChannel\r\na=ssrc:2024832766 mslabel:myDataChannel\r\na=ssrc:2024832766 label:myDataChannel\r\n"}} Got message {"type":"candidate","candidate":{"candidate":"candidate:2633341356 1 udp 2113937151 172.20.10.6 54721 typ host generation 0 ufrag aWnc+x1ot0kpmCj6","sdpMid":"data","sdpMLineIndex":0}} candidate added 

这里是client.js的代码:

我怎样才能确保每个客户真正连接到另一个,并且答案/ SDP是正确的? 对此有任何提示:也许渠道创造过早,只能在整个“握手”之后才能完成。 非常感谢

__编辑Jib的第一个答案后__

 var connectedUser, myConnection, dataChannel; //when a user clicks the login button loginBtn.addEventListener("click", function(event) { name = loginInput.value; send({ type: "login", name: name }); }); //handle messages from the server connection.onmessage = function (message) { console.log("Got message", message.data); var data = JSON.parse(message.data); switch(data.type) { case "login": onLogin(data.success); break; case "offer": onOffer(data.offer, data.name); break; case "answer": onAnswer(data.answer); break; case "candidate": onCandidate(data.candidate); break; default: break; } }; //when a user logs in function onLogin(success) { if (success === false) { alert("oops...try a different username"); } else { //creating our RTCPeerConnection object var configuration = { "iceServers": [{ "urls": "stun:stun.1.google.com:19302" }] }; myConnection = new webkitRTCPeerConnection(configuration, { optional: [{RtpDataChannels: true}] }); //ondatachannel is defined a bit later, commented out this line. //myConnection.ondatachannel = event => dataChannel = event.channel; console.log("RTCPeerConnection object was created"); console.log(myConnection); //setup ice handling //when the browser finds an ice candidate we send it to another peer myConnection.onicecandidate = function (event) { if (event.candidate) { send({ type: "candidate", candidate: event.candidate }); } }; myConnection.oniceconnectionstatechange = e => console.log(myConnection.iceConnectionState); myConnection.ondatachannel = function(ev) { console.log('Data channel is created!'); ev.channel.onopen = function() { console.log('Data channel is open and ready to be used.'); }; } } }; connection.onopen = function () { console.log("Connected"); }; connection.onerror = function (err) { console.log("Got error", err); }; // Alias for sending messages in JSON format function send(message) { if (connectedUser) { message.name = connectedUser; } connection.send(JSON.stringify(message)); }; //setup a peer connection with another user connectToOtherUsernameBtn.addEventListener("click", function () { var otherUsername = otherUsernameInput.value; connectedUser = otherUsername; if (otherUsername.length > 0) { //Create channel before sending the offer openDataChannel(); //make an offer myConnection.createOffer(function (offer) { send({ type: "offer", offer: offer }); myConnection.setLocalDescription(offer); }, function (error) { alert("An error has occurred.:", error); }); } }); //when somebody wants to call us function onOffer(offer, name) { connectedUser = name; myConnection.setRemoteDescription(new RTCSessionDescription(offer)); myConnection.createAnswer(function (answer) { myConnection.setLocalDescription(answer); send({ type: "answer", answer: answer }); }, function (error) { alert("oops...error: ", error); }); } //when another user answers to our offer function onAnswer(answer) { myConnection.setRemoteDescription(new RTCSessionDescription(answer)); } //when we got ice candidate from another user function onCandidate(candidate) { myConnection.addIceCandidate(new RTCIceCandidate(candidate)); console.log("candidate added"); } //creating data channel function openDataChannel() { var dataChannelOptions = { reliable:true }; dataChannel = myConnection.createDataChannel("myDataChannel", dataChannelOptions); console.log("Channel created"); dataChannel.onerror = function (error) { console.log("Error:", error); }; dataChannel.onmessage = function (event) { console.log("new message received"); console.log("Got message:", event.data); }; dataChannel.onopen = function() { console.log("channel opened"); }; } //when a user clicks the send message button sendMsgBtn.addEventListener("click", function (event) { console.log("send message"); var val = msgInput.value; dataChannel.send(val); }); 

数据渠道创build是不对称的,就像提供/答案交换一样。 只有提供者调用pc.createDataChannel() ,而pc.ondatachannel监听pc.ondatachannel

在调用createOffer之前,将createDataChannel调用移动到正确的createOffer ,然后添加到某处:

 myConnection.ondatachannel = event => dataChannel = event.channel; 

另外,使用dataChannel.onopen来学习何时打开通道(在两端工作)。

我怎样才能确保每个客户真正连接到另一个,并且答案/ SDP是正确的?

你可以做两件事情:

  1. 检查ICE连接状态(“检查”,“连接”):

    pc.oniceconnectionstatechange = e => console.log(pc.iceConnectionState);

  2. 添加错误callback。 像setLocalDescription调用可能会失败,并告诉你为什么,但你不检查失败。

删除后添加ondatachannel处理{optional: [{RtpDataChannels: true}]}:

 myConnection.onicecandidate = function (event) { if (event.candidate) { send({ type: "candidate", candidate: event.candidate }); } }; myConnection.ondatachannel = function(event) { var receiveChannel = event.channel; receiveChannel.onmessage = function(event) { console.log("ondatachannel message:", event.data); }; }; openDataChannel();