WebRTC 数据通道类型不匹配

Posted

技术标签:

【中文标题】WebRTC 数据通道类型不匹配【英文标题】:WebRTC Data channel type mismatch 【发布时间】:2021-12-07 16:08:00 【问题描述】:

我正在尝试在 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=s-s-rc:3090418315 cname:qaxoIuO1ybfnQaqY
a=s-s-rc:3090418315 msid:channel1 channel1
a=s-s-rc:3090418315 mslabel:channel1
a=s-s-rc: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);
;

我将不胜感激。

【问题讨论】:

【参考方案1】:

基于 RTP 的数据通道是一个非标准的仅限 Chrome 的扩展程序,并且已被弃用很长时间以支持 SCTP 数据通道(请参阅https://www.chromestatus.com/feature/6485681910054912)。您的 Brave 浏览器可能是一个非常旧的版本。

【讨论】:

以上是关于WebRTC 数据通道类型不匹配的主要内容,如果未能解决你的问题,请参考以下文章

带有手动信令的 WebRTC 数据通道,请举例?

WebRTC 数据通道未连接或未调用回调

通过 websocket 或使用 WebRTC 的数据通道逐个字符发送?

如何设置 WebRTC 数据通道最大比特率?

Webrtc 数据通道可以用于 Lync 和非 lync 用户之间的文件传输吗?

为 Windows 应用程序实现 webrtc 数据通道 [关闭]