当Firefox启动连接时,无法将WebRTC DataChannel与Chrome一起使用

时间:2014-02-07 19:06:09

标签: javascript html5 google-chrome interop webrtc

我正在尝试使用WebRTC DataChannels创建一个简单的网页,在浏览器之间发送ping / pongs。

Chrome 启动连接,然后 Chrome 连接时,工作
Firefox 启动连接,然后 Firefox 连接时,工作
Chrome 启动连接,然后 Firefox 连接时,工作
但是,当 Firefox 启动连接,然后 Chrome 连接时,无法正常工作。 Chrome从不接收Firefox发送的数据。

我在Archlinux上使用Firefox 26和Chromium 32。

这是我的JavaScript代码:

<!DOCTYPE html>
<html>
<head>
    <title>WebRTC test</title>
    <meta charset="utf-8">
</head>
<body>
    <button id="create" disabled>Create data channel</button>

    <script type="text/javascript">
        // DOM

        var create = document.getElementById('create');

        // Compatibility

        window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
        window.RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription;
        window.RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate || window.webkitRTCIceCandidate;

        // Create a WebRTC object

        var rtc = new RTCPeerConnection(null);

        // Create a data channel

        var sendChannel = rtc.createDataChannel('pingtest', {reliable: false});
        var myMsg = 'ping';

        function setRecvChannel(recvChannel) {
            recvChannel.onmessage = function(event) {
                if(event.data.indexOf('\x03\x00\x00\x00\x00\x00\x00\x00\x00') === 0) {
                    console.log('-> ' + window.btoa(event.data));
                    return; // Received channel's name, ignore
                }

                console.log('-> ' + event.data);
                window.setTimeout(function() {
                    console.log('<- ' + myMsg);
                    sendChannel.send(myMsg);
                }, 500);
            };
        }

        // Chrome and Firefox

        sendChannel.onopen = function(event) {
            setRecvChannel(sendChannel);

            if(myMsg === 'ping') {
                console.log('<- ' + myMsg);
                sendChannel.send(myMsg);
            }
        };

        // Firefox

        rtc.ondatachannel = function(event) {
            setRecvChannel(event.channel);
        };

        // ICE

        rtc.onicecandidate = function(event) {
            if(event.candidate) {
                console.log('<- ' + JSON.stringify(event.candidate));
                ws.send(JSON.stringify(event.candidate));
            }
        };

        // Signaling channel

        var ws = new WebSocket('ws://127.0.0.1:49300/');

        ws.onopen = function() {
            create.disabled = false;
        };

        ws.onmessage = function(event) {
            console.log('-> ' + event.data);
            var data = JSON.parse(event.data);

            if(data.sdp) {
                rtc.setRemoteDescription(new RTCSessionDescription(data));

                if(data.type === 'offer') {
                    myMsg = 'pong';

                    rtc.createAnswer(function(anwser) {
                        rtc.setLocalDescription(anwser, function () {
                            console.log('<- ' + JSON.stringify(anwser));
                            ws.send(JSON.stringify(anwser));
                        });
                    }, console.error);
                }
            }
            else {
                rtc.addIceCandidate(new RTCIceCandidate(data));
            }
        };

        ws.onclose = function() {
            create.disabled = true;
        };

        // Create an offer

        create.onclick = function() {
            rtc.createOffer(function(offer) {
                rtc.setLocalDescription(offer, function () {
                    offer.sdp = offer.sdp;
                    console.log(offer.sdp);
                    console.log('<- ' + JSON.stringify(offer));
                    ws.send(JSON.stringify(offer));
                });
            }, console.error);
        };
    </script>
</body>
</html>

以下是我仅为测试目的而创建的基于WebSocket的信令服务器,它只是侦听端口49300并将从客户端接收的数据广播到其他客户端:

#!/usr/bin/python
#-*- encoding: Utf-8 -*-
from socket import socket, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR
from string import printable
from threading import Thread
from base64 import b64encode
from struct import unpack
from hashlib import sha1

PORT = 49300

activeSocks = []

def SignalingChannel(ip, port, sock):
    print 'Connection from %s:%s' % (ip, port)

    # Handling the HTTP request

    try:
        headers = sock.recv(8184)

        assert headers.upper().startswith('GET')
        assert headers.endswith('\r\n\r\n')

        data = headers.strip().replace('\r', '').split('\n')[1:]

        headers = {}
        for header in data:
            name, value = header.split(':', 1)
            headers[name.strip().lower()] = value.strip()

        assert headers['host']
        assert 'upgrade' in headers['connection'].lower()
        assert 'websocket' in headers['upgrade'].lower()
        assert headers['sec-websocket-version'] == '13'
        assert len(headers['sec-websocket-key']) == 24

        guid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
        accept = b64encode(sha1(headers['sec-websocket-key'] + guid).digest())

        sock.send('HTTP/1.1 101 Switching Protocols\r\n' +
        'Connection: Upgrade\r\n' +
        'Upgrade: websocket\r\n' +
        'Sec-WebSocket-Accept: %s\r\n' % accept +
        '\r\n')

    except:
        try:
            msg = 'This is a RFC 6455 WebSocket server.\n'

            sock.send('HTTP/1.1 400 Bad Request\r\n' +
            'Connection: Close\r\n' +
            'Content-Length: %d\r\n' % len(msg) +
            'Content-Type: text/plain; charset=us-ascii\r\n' +
            'Sec-WebSocket-Version: 13\r\n' +
            '\r\n' +
            msg)

        except:
            pass

        sock.close()
        print 'Disconnection from %s:%s' % (ip, port)
        return

    activeSocks.append(sock)

    try:
        data = sock.recv(2)
        while len(data) == 2:
            frame = data[0] + chr(ord(data[1]) & 0b01111111)
            opcode = ord(data[0]) & 0b00001111
            mask = ord(data[1]) & 0b10000000
            paylen = ord(data[1]) & 0b01111111

            if paylen == 126:
                data = sock.recv(2)
                frame += data
                paylen = unpack('>H', data)[0]
            elif paylen == 127:
                data = sock.recv(8)
                frame += data
                paylen = unpack('>Q', data)[0]

            if mask:
                mask = sock.recv(4)

            data = ''
            received = True
            while received and len(data) < paylen:
                received = sock.recv(paylen - len(data))
                data += received

            if mask:
                unmasked = ''
                for i in xrange(len(data)):
                    unmasked += chr(ord(data[i]) ^ ord(mask[i % 4]))
            else:
                unmasked = data

            frame += unmasked

            if opcode != 8:
                print '-- From port %d --' % port
                if all(ord(c) < 127 and c in printable for c in unmasked):
                    print unmasked
                else:
                    print repr(unmasked)
                for destSock in activeSocks:
                    if destSock != sock:
                        destSock.send(frame)
            else:
                break

            data = sock.recv(2)
    except:
        pass

    activeSocks.remove(sock)
    sock.close()
    print 'Disconnection from %s:%s' % (ip, port)

listenSock = socket(AF_INET, SOCK_STREAM)
listenSock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

listenSock.bind(('0.0.0.0', PORT))
listenSock.listen(20)

print 'Listening on port 49300...'

while True:
    clientSock, (ip, port) = listenSock.accept()
    Thread(target=SignalingChannel, args=(ip, port, clientSock)).start()

要运行代码,启动信令服务器,在两个浏览器选项卡中打开网页,单击&#34;创建数据通道&#34;按钮并查看Web控制台。

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

通过浏览Chrome / Firefox错误跟踪器,我们已经确定并解决了此问题,但仅限于Chrome Canary 33.0.1715.0或更高版本。

如果您不愿意要求上面提到的Chrome Canary版本,您可以检测到错误的对等组合,并让“优惠”按钮通知其他客户提出要约。

伪代码:

socket.onMessage(msg) {
   if(msg == "request-offer"){
      doOffer();
   }
   ...
}

createDataChannelButton.onClick() {
   if(!canCreateChannelBasedOnBrowser){
      socket.send("request-offer");
   }
   else {
      doOffer();
   }
}

使用您的示例代码:

<!DOCTYPE html>
<html>
<head>
   <title>WebRTC test</title>
   <meta charset="utf-8">
</head>
<body>
<button id="create" disabled>Create data channel</button>

<script type="text/javascript">
   // DOM

   // CHANGE: Add basic browser detection based on google's adapter.js file.
   var rtcBrowserVersion = 0;
   var rtcCanInitiateDataOffer = false;
   if (navigator.mozGetUserMedia) {
      rtcBrowserVersion = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
      rtcCanInitiateDataOffer = true;
   } else if (navigator.webkitGetUserMedia) {
      // Chrome Canary reports major version 35 for me.  Can't find a reliable resource to confirm
      // canary versions.
      rtcBrowserVersion = parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10);
      rtcCanInitiateDataOffer = rtcBrowserVersion >= 35;
   }


   var create = document.getElementById('create');

   // Compatibility

   window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
   window.RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription;
   window.RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate || window.webkitRTCIceCandidate;

   // Create a WebRTC object

   var rtc = new RTCPeerConnection(null);

   // Create a data channel

   var sendChannel = rtc.createDataChannel('pingtest', {reliable: false});
   var myMsg = 'ping';

   function setRecvChannel(recvChannel) {
      recvChannel.onmessage = function(event) {
         if(event.data.indexOf('\x03\x00\x00\x00\x00\x00\x00\x00\x00') === 0) {
            console.log('-> ' + window.btoa(event.data));
            return; // Received channel's name, ignore
        }

        console.log('-> ' + event.data);
        window.setTimeout(function() {
           console.log('<- ' + myMsg);
           sendChannel.send(myMsg);
        }, 500);
     };
  }

  // Chrome and Firefox

  sendChannel.onopen = function(event) {
     setRecvChannel(sendChannel);

     if(myMsg === 'ping') {
        console.log('<- ' + myMsg);
        sendChannel.send(myMsg);
     }
  };

  // Firefox

  rtc.ondatachannel = function(event) {
     setRecvChannel(event.channel);
  };

  // ICE

  rtc.onicecandidate = function(event) {
     if(event.candidate) {
        console.log('<- ' + JSON.stringify(event.candidate));
        ws.send(JSON.stringify(event.candidate));
     }
  };

  // Signaling channel

  var ws = new WebSocket('ws://127.0.0.1:49300/');

  ws.onopen = function() {
     create.disabled = false;
  };

  ws.onmessage = function(event) {
     console.log('-> ' + event.data);
     var data = JSON.parse(event.data);

    if(data.sdp) {
       rtc.setRemoteDescription(new RTCSessionDescription(data));

       if(data.type === 'offer') {
          myMsg = 'pong';

          rtc.createAnswer(function(anwser) {
             rtc.setLocalDescription(anwser, function () {
                console.log('<- ' + JSON.stringify(anwser));
                ws.send(JSON.stringify(anwser));
             });
          }, console.error);
       }
    }
    // CHANGE: Chrome with offer bug asked to initiate the offer.
    else if(data.initiate === true){
       doOffer();
    }
    else {
       rtc.addIceCandidate(new RTCIceCandidate(data));
    }
 };

 ws.onclose = function() {
   create.disabled = true;
};

// Create an offer

// CHANGE: Create function for offer, so that it may be called from ws.onmessage
function doOffer(){
   rtc.createOffer(function(offer) {
      rtc.setLocalDescription(offer, function () {
         offer.sdp = offer.sdp;
         console.log(offer.sdp);
         console.log('<- ' + JSON.stringify(offer));
         ws.send(JSON.stringify(offer));
      });
   }, console.error);
}

create.onclick = function() {
   // CHANGE: If this client is not able to negotiate a data channel, send a
   // message to the peer asking them to offer the channel.
   if(rtcCanInitiateDataOffer){
      doOffer();
   }
   else {
      ws.send(JSON.stringify({initiate:true}));
   }
};

   

相关问题:

还值得注意的是,当向Chrome对等方发送数据时,目前有max message buffer size of 16KB。如果要发送大数据消息,则需要先将它们分成16KB块,然后再通过数据通道传输。

答案 1 :(得分:0)

仅供参考,我进一步修改了HTML以尝试解决我正在与C ++ libjingle(Chrome和Firefox)谈论的问题;)

此版本显示页面状态。不漂亮,但有用。

  <!DOCTYPE html>
  <html>
  <head>
      <title>WebRTC test</title>
      <meta charset="utf-8">
  </head>
  <body>
      <button id="create" disabled>Create data channel</button>

  <div id="output1">output1</div>
  <div id="output2">output2</div>
  <div id="output3">output3</div>
  <div id="output4">output4</div>
  <div id="output5">output5</div>
  <div id="output6">output6</div>
  <div id="output7">output7</div>
  <div id="output8">output8</div>


  <script type="text/javascript">
     var myCounts = {
         output1: 0,
         output2: 0
     };

     var server1 = 'ws://127.0.0.1:49300/';

     // CHANGE: Add basic browser detection based on google's adapter.js file.
     var rtcBrowserVersion = 0;
     var rtcCanInitiateDataOffer = false;
     if (navigator.mozGetUserMedia) {
        rtcBrowserVersion = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
        rtcCanInitiateDataOffer = true;
     } else if (navigator.webkitGetUserMedia) {
        // Chrome Canary reports major version 35 for me.  Can't find a reliable resource to confirm
        // canary versions.
        rtcBrowserVersion = parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10);
        rtcCanInitiateDataOffer = rtcBrowserVersion >= 35;
     }

     // DOM

     var create = document.getElementById('create');

     // Compatibility

     window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
     window.RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription;
     window.RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate || window.webkitRTCIceCandidate;

     // Create a WebRTC object

     var rtc = new RTCPeerConnection(null);

     // Create a data channel

     var sendChannel = rtc.createDataChannel('PingTest', {reliable: false});
     var myMsg = 'ping';

     function setRecvChannel(recvChannel) {
        recvChannel.onmessage = function(event) {
           myCounts.output1++;
           document.getElementById("output1").innerHTML = myCounts.output1 + ": " + event.data;

           if(event.data.indexOf('\x03\x00\x00\x00\x00\x00\x00\x00\x00') === 0) {
              console.log('-> ' + window.btoa(event.data));
              return; // Received channel's name, ignore
           };

           console.log('-> ' + event.data);
           window.setTimeout(function() {
               console.log('<- ' + myMsg);
               sendChannel.send(myMsg);
             }, 500);
        };
     }

     // Chrome and Firefox

     sendChannel.onopen = function(event) {
        setRecvChannel(sendChannel);

        if(myMsg === 'ping') {
           console.log('<- ' + myMsg);
           sendChannel.send(myMsg);
        }
     };

     // Firefox

     rtc.ondatachannel = function(event) {
         myCounts.output2++;
         document.getElementById("output2").innerHTML = myCounts.output2 + " channel: " + event.channel.label;
         setRecvChannel(event.channel);
     };

     // ICE

     rtc.onicecandidate = function(event) {
        if(event.candidate) {
           console.log('<- ' + JSON.stringify(event.candidate));
           ws.send(JSON.stringify(event.candidate));
        }
     };

     // Signaling channel

     var ws = new WebSocket(server1);
     document.getElementById("output3").innerHTML="created WebSocket (not connected) " + server1;

     ws.onopen = function() {
        create.disabled = false;
        document.getElementById("output3").innerHTML="onOpen WebSocket " + server1;
     };

     ws.onmessage = function(event) {
        document.getElementById("output3").innerHTML="onMessage WebSocket " + event.data;
        console.log('-> ' + event.data);
        var data = JSON.parse(event.data);

        if (data.sdp) {
           rtc.setRemoteDescription(new RTCSessionDescription(data));

           if (data.type === 'offer') {
               document.getElementById("output4").innerHTML="received SessiionDescription offer";
               myMsg = 'pong';

              rtc.createAnswer(function(anwser) {
                rtc.setLocalDescription(anwser, function () {
                    console.log('<- ' + JSON.stringify(anwser));
                    ws.send(JSON.stringify(anwser));
                });
              }, console.error);
           }
           else if (data.type == 'answer') {
               document.getElementById("output6").innerHTML="received SessiionDescription answer";
           };
        }
        // CHANGE: Chrome with offer bug asked to initiate the offer.
        else if (data.reverseInitiate === true) {
           document.getElementById("output8").innerHTML="was asked to reverseInitiate, I doOffer";
           doOffer();
        }
        else {
           rtc.addIceCandidate(new RTCIceCandidate(data));
        }
     };

     ws.onclose = function() {
         create.disabled = true;
     };

     // Create an offer

     // CHANGE: Create function for offer, so that it may be called from ws.onmessage
     function doOffer(){
        rtc.createOffer(function(offer) {
           rtc.setLocalDescription(offer, function () {
              offer.sdp = offer.sdp;
              console.log(offer.sdp);
              console.log('<- ' + JSON.stringify(offer));
              ws.send(JSON.stringify(offer));
           });
        }, console.error);
     }

     create.onclick = function() {
        // CHANGE: If this client is not able to negotiate a data channel, send a
        // message to the peer asking them to offer the channel.
        if (rtcCanInitiateDataOffer){
           doOffer();
        }
        else {
           document.getElementById("output7").innerHTML="sending reverseInitiate";
           ws.send(JSON.stringify( {reverseInitiate:true} ));
        };
     };
  </script>

  </body>
  </html>