WebRTC: RTCDataChannel is not 'open'

I followed the complete WebRTC tutorial and implemented a simple p2p chat. My alarm server is running away from localhost: 9090. When I try to send a message, I get:

RTCDataChannel.readyState is not 'open' 

However, the connection seems to be set up correctly:

 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 

Here is the client.js code:

How can I make sure that each client is really connected to another and that the / SDP answer is correct? Any advice for this: is it possible that the creation of the channel was done too early and should be done only after all the “handshakes”? Many thanks

__ EDIT After Jib 1st answer __

 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); }); 
+7
javascript webrtc rtcp
source share
2 answers

Creating a data channel is asymmetric, just like an offer / response exchange. Only the provider calls pc.createDataChannel() , and the responder listens to pc.ondatachannel .

Move the createDataChannel call right before you call createOffer and add somewhere:

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

Also, use dataChannel.onopen to find out when the channel is open (running on both ends).

How can I make sure that each client is really connected to another and that the answer / SDP is correct?

You can do two things:

  • Check the status of the ICE connection ("check", "connected"):

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

  • Add callbacks. Calls like setLocalDescription may fail and tell you why, but you do not check for a failure.

+3
source share

Add ondatachannel processing after removing {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(); 
+1
source share

All Articles