2016-07-04 8 views
7

Śledziłem cały tutorial o WebRTC i wdrażaniu prostego czatu p2p. Mój serwer sygnalizacyjny pracuje na localhost: 9090. Gdy próbuję wysłać wiadomość, ja otrzymuję:WebRTC: RTCDataChannel nie jest "otwarty"

RTCDataChannel.readyState is not 'open' 

Jednak połączenie wydaje się zostały ustalone odpowiednio:

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 

Oto kod client.js:

Jak mogę się upewnić, że każdy klient jest naprawdę podłączony do drugiego, a odpowiedź/SDP jest poprawna? Wszelkie wskazówki na ten temat: może tworzenie kanału zostało wykonane zbyt wcześnie i powinno być zrobione dopiero po "uzgadnianiu"? Dzięki dużo

__ EDIT Po 1 odpowiedź foka za __

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); 
    }); 
+1

Usuń 'RtpDataChannels'. Te rzeczy są przestarzałe, tylko w Chrome i prawdopodobnie nie działają. – jib

+0

Dzięki temu mogę wysłać dane od użytkownika, który inicjuje połączenie (kto utworzył datachannel). Jak mogę odesłać dane do drugiego użytkownika, ponieważ powiedziałeś mi, że tylko 1 musi utworzyć kanał danych? – Arkon

+0

Kanały danych są dwukierunkowe, więc po prostu dodaj 'dataChannel.onmessage' do odbioru. – jib

Odpowiedz

3

stworzenia kanału danych jest asymetryczny, podobnie jak wymiana oferta/odpowiedź. Tylko oferent dzwoni pod numer pc.createDataChannel(), a osoba, która go wysłała, nasłuchuje na pc.ondatachannel.

Najedź połączenia createDataChannel do prawej przed wywołaniem createOffer i dodaj gdzieś:

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

Ponadto, należy dataChannel.onopen aby dowiedzieć się, gdy kanał jest otwarty (działa na obu końcach).

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

Można zrobić dwie rzeczy:

  1. Sprawdzić stan połączeń ICE ("Kontrola", "połączony"):

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

  2. Dodaj zwrotnych o błędach. Połączenia takie jak setLocalDescription mogą zawieść i informować o przyczynach, ale nie sprawdzasz, czy nie powiodło się.

+0

Wielkie dzięki! Więc zredagowałem moją główną wiadomość z twoimi zmianami. Przenieśliłem {openDataChannel} tuż przed {myConnection.createOffer}. Dodałem również mójConnection.ondatachannel (zdefiniowałem go z tą samą składnią co w oficjalnym dokumencie api). Dodałem {dataChannel.onnopen} i {myConnection.oniceconnectionstatechange}. Nadal mam ten sam problem w rzeczywistości. Proces jest następujący: zalogowałem użytkownika 1, a następnie użytkownika 2. użytkownik1 nawiązuje połączenie i wysyła wiadomość. {client.js: 178 Uncaught InvalidStateError: Nie powiodło się wykonanie 'send' w 'RTCDataChannel': RTCDataChannel.readyState nie jest 'otwarty'} w linii 178. – Arkon

+0

Wielkie dzięki! byłem po tej instrukcji: https://www.tutorialspoint.com/webrtc/webrtc_text_demo.htm ale ist brakowało z wręczeniem dataChannel. jak yout didi z myConnection.ondatachannel = event => dataChannel = event.channel; – jvoigt

1

Dodaj ondatachannel obsługę po usunięciu {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();