WebRTC:RTCDataChannel 不是 'open'
WebRTC: RTCDataChannel is not 'open'
我遵循了有关 WebRTC 和实现简单 p2p 聊天的整个教程。我的信令服务器正在 localhost:9090 上工作。
当我尝试发送消息时,我收到:
RTCDataChannel.readyState is not 'open'
但是,连接似乎已经正确建立:
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 是正确的?对此的任何提示:也许频道创建完成得太早,应该只在整个 "handshake" 之后完成?非常感谢
__ 在 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);
});
数据通道创建是不对称的,就像 offer/answer 交换一样。只有提议者呼叫 pc.createDataChannel()
,而应答者则听 pc.ondatachannel
.
将您的 createDataChannel
调用移至调用 createOffer
之前的右侧,并在某处添加:
myConnection.ondatachannel = event => dataChannel = event.channel;
此外,使用dataChannel.onopen
了解频道何时打开(两端均有效)。
How can I make sure that each client is really connected to the other and that the answer / SDP was correct?
你可以做两件事:
检查ICE连接状态("checking", "connected"):
pc.oniceconnectionstatechange = e => console.log(pc.iceConnectionState);
添加错误回调。 setLocalDescription
之类的调用可能会失败,并会告诉您原因,但您不会检查失败。
删除{optional: [{RtpDataChannels: true}]}:
后添加ondatachannel
处理
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();
我遵循了有关 WebRTC 和实现简单 p2p 聊天的整个教程。我的信令服务器正在 localhost:9090 上工作。 当我尝试发送消息时,我收到:
RTCDataChannel.readyState is not 'open'
但是,连接似乎已经正确建立:
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 是正确的?对此的任何提示:也许频道创建完成得太早,应该只在整个 "handshake" 之后完成?非常感谢
__ 在 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);
});
数据通道创建是不对称的,就像 offer/answer 交换一样。只有提议者呼叫 pc.createDataChannel()
,而应答者则听 pc.ondatachannel
.
将您的 createDataChannel
调用移至调用 createOffer
之前的右侧,并在某处添加:
myConnection.ondatachannel = event => dataChannel = event.channel;
此外,使用dataChannel.onopen
了解频道何时打开(两端均有效)。
How can I make sure that each client is really connected to the other and that the answer / SDP was correct?
你可以做两件事:
检查ICE连接状态("checking", "connected"):
pc.oniceconnectionstatechange = e => console.log(pc.iceConnectionState);
添加错误回调。
setLocalDescription
之类的调用可能会失败,并会告诉您原因,但您不会检查失败。
删除{optional: [{RtpDataChannels: true}]}:
ondatachannel
处理
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();