本地WebRTC连接卡在"检查"离线时

时间:2018-05-20 03:24:06

标签: javascript webrtc serverless

我一直在尝试获得一个医疗开发环境,以便在当地工作。在没有互联网的火车上的一台电脑上。我创造了这个最小的" Hello World"尝试在同一页面中的两件事之间创建WebRTC连接的页面("创建者"和#34; joiner")。这样,信令服务器被删除,并且步骤可以在一个同步日志中显示。但是,当我的计算机离线时,我并没有得到我期望的回调。

<!doctype html>
<html>
<head>
  <meta charset="utf-8">

  <title>Offline WebRTC</title>
  <style>
  html, body {padding:0;margin:0;height:100%}
  body {box-sizing:border-box;padding:50px 0 0px;color:#ccc;background-color:#303030;}
  h1   {position:fixed;margin:0;line-height:50px;padding:0 15px;top:0;left:0;font-size:18px;width:100%;box-sizing:border-box}
  </style>
</head>

<body>
<h1>Why does this WebRTC work online but not offline?</h1>
<pre id="log"></pre>


<script type="text/javascript">

//
// Gobals
//

// This is the interface through which the Creator and Joiner communicate.
// Usually this would involve piping through the server via websockets.
const signallingServer = {
  giveOfferToJoiner: null,   // initialized in the "create" section
  giveAnswerToCreator: null, // initialized in the "join" section
};

let logCounter = 0;
function logWithIndent(message, indent) {
  const prefix = ''.padStart(indent, ' ') + (''+logCounter).padStart(4, '0') + ' ';
  logCounter += 1;
  document.getElementById('log').textContent += prefix + message + '\n';
  const panes = [
    document.getElementById('join-pane'),
    document.getElementById('create-pane'),
  ];
}


//
// Join (right column)
//
(() => {
  const log = (message) => logWithIndent(message, 50);
  const pc = new RTCPeerConnection(null);
  const sdpConstraints = { optional: [{RtpDataChannels: true}]  };



  signallingServer.giveOfferToJoiner = (offerString) => {
    log('Received offer');
    const offerDesc = new RTCSessionDescription(JSON.parse(offerString));
    pc.setRemoteDescription(offerDesc);
    pc.createAnswer(
      (answerDesc) => {
        log('Setting peer connection description')
        pc.setLocalDescription(answerDesc);
      },
      () => { log("ERROR: Couldn't create answer"); },
      sdpConstraints
    );
  };

  pc.ondatachannel  = (e) => {
    const dataChannel = e.channel;
    const sendMessage = (message) => {
      log(`Sending message: ${message}`);
      dataChannel.send(message);
    };
    dataChannel.onopen = () => { log("Data channel open!"); };
    dataChannel.onmessage = (e) => {
      const message = e.data
      log("Received message: " + message);
      sendMessage('PONG: ' + message)
    }

  };
  pc.onicecandidate = (e) => {
    if (e.candidate) {
      log('waiting for null candidate for answer');
      return;
    }
    const answer = JSON.stringify(pc.localDescription);
    log('Answer created. Sending to creator');
    signallingServer.giveAnswerToCreator(answer);
    log('waiting for connection...')
  };
  pc.oniceconnectionstatechange = (e) => {
    const state = pc.iceConnectionState;
    log(`iceConnectionState changed to "${state}"`)
    if (state == "connected") {
      log('TODO: send message');
    }
  };

  log(`Waiting for offer`);
})();


//
// Create (left)
//
(() => {
  const log = (message) => logWithIndent(message, 0);
  const pc = new RTCPeerConnection(null);

  let dataChannel = null;
  const sendMessage = (message) => {
    log(`Sending message: ${message}`);
    dataChannel.send(message);
  };

  signallingServer.giveAnswerToCreator = (answerString) => {
    var answerDesc = new RTCSessionDescription(JSON.parse(answerString));
    log('Setting peer connection description')
    pc.setRemoteDescription(answerDesc);
  };


  pc.oniceconnectionstatechange = (e) => {
    const state = pc.iceConnectionState;
    log(`iceConnectionState changed to "${state}"`)
  };
  pc.onicecandidate = (e) => {
    if (e.candidate) {
      log(`Waiting for null candidate for offer`);
      return;
    }
    const offer = JSON.stringify(pc.localDescription);
    log(`Offer created. Sending to joiner`);
    signallingServer.giveOfferToJoiner(offer);
    log(`waiting for answer...`);
  }


  function createOffer() {
    dataChannel = pc.createDataChannel("chat");
    dataChannel.onopen = () => { log("Data channel open!"); sendMessage('Hello World!')};
    dataChannel.onmessage = (e) => { log("Received message: " + e.data); }
    log('Creating offer...');
    pc.createOffer().then((e) => {
      log('setting local description');
      pc.setLocalDescription(e);
    });
  };


  createOffer();
})();
</script>

</body>
</html>

重现:

  1. 连接到互联网时,请在本地打开此.html文件(应该有file://...网址,不需要服务器)
  2. 观察它是否正常工作(应该到PONG: Hello World!
  3. 断开计算机与互联网的连接
  4. 刷新页面
  5. 观察iceConnectionState changed to "checking"
  6. 后不会继续

    其他信息:

    • 与&#34;离线&#34;相比,断开我的电脑与互联网的连接会产生不同的影响。 chrome devtools的网络选项卡中的复选框。选中此复选框不会影响是否可以建立连接。

    所以我的主要问题是:如何在计算机离线时打开本地WebRTC连接?

    其他问题:我假设浏览器试图在后台与某人交谈,作为检查或连接步骤的一部分。它试图与谁交谈?为什么这些请求没有显示在devtools的网络选项卡中?

1 个答案:

答案 0 :(得分:1)

WebRTC从本地网络接口收集候选人,作为ICE流程的一部分。 从查看SDP(在调试器中或在chrome:// webrtc-interals上),当离线时没有接口(除了被忽略的环回接口)来收集候选者,onicecandidate中没有候选者而你只是在没有任何候选人的情况下发送报价。

进入&#39;检查&#39; ICE连接状态似乎是一个bug,https://w3c.github.io/webrtc-pc/#rtcicetransportstate需要一个远程候选者。