我正在尝试在 playCanvas 中实现 WebRTC 通信,但在建立 Peers 之间的连接时遇到了麻烦。
从本地环境开始,我使用的是 Brave Browser 和 Ubuntu OS,连接已建立,我可以在对等点之间发送消息。以下是使用正确的协议 RTP 创建的正确报价。
"v=0
o=- 6768969287305795266 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0
a=msid-semantic: WMS
m=application 9 UDP/TLS/RTP/SAVPF 118
c=IN IP4 0.0.0.0
b=AS:30
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:kpEN
a=ice-pwd:0cEFgezZPzFBSsTbtx03XZUv
a=ice-options:trickle
a=fingerprint:sha-256 CD:93:28:C9:A1:17:59:C7:3C:E3:F9:03:48:02:59:08:50:FF:A1:8A:AF:53:9B:65:3B:62:B0:5E:E6:23:46:9F
a=setup:actpass
a=mid:0
a=sendrecv
a=msid:channel1 channel1
a=rtcp-mux
a=rtpmap:118 google-data/90000
a=ssrc:3090418315 cname:qaxoIuO1ybfnQaqY
a=ssrc:3090418315 msid:channel1 channel1
a=ssrc:3090418315 mslabel:channel1
a=ssrc:3090418315 label:channel1
"
这是有效的报价,在本地 PC 上,我可以在对等方之间连接和发送消息。但这仅适用于 Brave 浏览器,如果我打开 Chrome 或另一台带有 Brave 浏览器的 PC,它将无法工作,而且如果我在 playcanvas 上实现 WebRTC,它也将无法工作。
以下是我从报价中获得的数据,我认为这是不工作的主要原因。因为它使用SCTP协议。
"v=0
o=- 5811252825165405755 3 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0
a=extmap-allow-mixed
a=msid-semantic: WMS
m=application 9 UDP/DTLS/SCTP webrtc-datachannel
c=IN IP4 0.0.0.0
a=candidate:1528355922 1 udp 2113937151 920a8f71-bd47-4dac-9a24-497783686050.local 59907 typ host generation 0 network-cost 999
a=ice-ufrag:NjXz
a=ice-pwd:M3FdXA3wNEA25FQaFU3GwcVd
a=ice-options:trickle
a=fingerprint:sha-256 F0:59:BF:FF:AB:8B:4A:1A:A2:D8:0E:57:DD:AD:7B:14:24:7E:A7:6F:CC:C3:B2:BB:83:96:BB:53:49:71:CE:9A
a=setup:actpass
a=mid:0
a=sctp-port:5000
a=max-message-size:262144
"
这是我在 playcanvas 上收到的错误。
Uncaught (in promise) DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote offer sdp: Data channel type mismatch. Expected RTP, got SCTP.
下面是 playcanvas 内部的实现。
NetworkManager.id = null;
NetworkManager.socket = null;
NetworkManager.clients = null;
NetworkManager.user = null;
NetworkManager.username = null;
NetworkManager.connectedUser = null;
NetworkManager.yourConn = null;
NetworkManager.dataChannel = null;
// initialize code called once per entity
NetworkManager.prototype.initialize = function() {
// Context
var self = this;
this.socket = new WebSocket(this.address, "wss");
this.clients = [];
// Listeners
this.socket.onopen = function(event){
self.initState();
};
this.socket.onmessage = function(message) {
const data = JSON.parse(message.data);
switch (data.type) {
case 'login':
self.handleLogin(data.success, data.name);
break;
case 'offer':
self.handleOffer(data.offer, data.name);
break;
case 'answer':
self.handleAnswer(data.answer);
break;
case 'candidate':
self.handleCandidate(data.candidate);
break;
default:
break;
}
};
this.socket.onerror = function (err) {
console.log('Got error', err);
};
};
// Called every frame
NetworkManager.prototype.update = function(dt) {
// this.updatePosition();
// if(this.app.keyboard.wasPressed(pc.KEY_SPACE)){
// this.sendPayload("Soldo");
// }
};
// Functions
NetworkManager.prototype.handleMembers = function(users) {
this.clients = users;
this.initializePlayers(this.clients);
};
NetworkManager.prototype.handleLogin = function(success, username) {
if (!success) {
alert('try different username');
} else {
this.clients.push({username});
var configuration = {
iceServers: [{ url: 'stun:stun2.1.google.com:19302' }],
};
this.yourConn = new RTCPeerConnection(configuration, {
optional: [{ RtpDataChannels: true }],
});
this.yourConn.onicecandidate = event => {
if (event.candidate) {
this.sendMessage({
type: 'candidate',
candidate: event.candidate,
});
}
};
this.dataChannel = this.yourConn.createDataChannel('channel1', {
reliable: true,
});
this.dataChannel.onerror = function(error) {
console.log('Error: ', error);
};
this.dataChannel.onmessage = function(event) {
console.log('on message data channel');
console.log(event.data.data);
};
this.dataChannel.onclose = () => {
console.log('data channel is closed.');
};
}
};
NetworkManager.prototype.login = function(name) {
username = name;
if (username.length > 0) {
this.sendMessage({
type: 'login',
name: username,
});
}
};
NetworkManager.prototype.createOffer = function(name) {
const callToUsername = name;
if (callToUsername.length > 0) {
this.connectedUser = callToUsername;
this.yourConn.createOffer(
offer => {
this.sendMessage({
type: 'offer',
offer: offer,
});
this.yourConn.setLocalDescription(offer);
},
error => {
alert('Error when creating an offer');
}
);
}
};
NetworkManager.prototype.sendMessage = function(message) {
if (this.connectedUser) {
message.name = this.connectedUser;
}
this.socket.send(JSON.stringify(message));
};
NetworkManager.prototype.initState = function() {
// this.login(this.username);
};
NetworkManager.prototype.handleCandidate = function(candidate) {
this.yourConn.addIceCandidate(new RTCIceCandidate(candidate));
};
NetworkManager.prototype.handleOffer = function(offer, name) {
this.connectedUser = name;
this.yourConn.setRemoteDescription(new RTCSessionDescription(offer));
this.yourConn.createAnswer(
answer => {
this.yourConn.setLocalDescription(answer);
this.sendMessage({
type: 'answer',
answer: answer,
});
},
error => {
alert('Error while creating an answer');
}
);
};
NetworkManager.prototype.handleAnswer = function(answer) {
this.yourConn.setRemoteDescription(new RTCSessionDescription(answer));
};
NetworkManager.prototype.sendPayload = function (message) {
this.dataChannel.send(message);
};
我将不胜感激。