如何在Chrome/node-webkit中重置WebRTC状态而不刷新页面?

7

问题:

在Chrome中,当WebRTC组件被踢到无效状态时,如何重置它们的状态 - 而不重新加载页面?请参阅下面有关我如何复制此状态以及为什么我会问这个问题的更多详细信息:

问题描述:

在进行呼叫时,我在Chrome 35 / node-webkit 0.10.0中设置Ice Candidates时遇到以下错误:

Failed to execute 'addIceCandidate' on 'RTCPeerConnection': The ICE candidate could not be added.

现在,我知道为什么会发生这种情况。 我正在开发一个健壮的WebRTC应用程序,可以处理一些正常用户滥用。 为了复制此状态,我基本上必须进行几次WebRTC呼叫,然后快速杀死它们,然后立即尝试另一个呼叫。 我猜这一定会将PeerConnection和其他组件踢入期望发生B的不同状态,但我再次从A开始。 这由以下错误消息表明:

Failed to set session description: Failed to set remote answer sdp: Called in wrong state: STATE_INIT

现在,我们在互联网上看到的大多数WebRTC演示,例如http://apprtc.appspot.com,是无状态的,并且经常刷新浏览器,这会导致重置DOM状态。 因此,对于那些开发人员来说,答案很简单。 只需重新加载页面即可。

目前,当DOM进入此状态时,我必须重新加载应用程序。 但是,这不是一个可接受的解决方案,因为我正在构建一个单页应用程序,而不是一个网站,

我想知道是否有一种方法可以调用API来告诉它重置抛出这些错误的任何内容的状态?

一些故障排除步骤:

我尝试了以下JavaScript控制台中的操作,以在node-webkit(Chrome 35)中手动重置PeerConnection的状态,但没有帮助:

 var properties = {};
 properties.pcConfig = {
     "iceServers": [{
         "url": "stun:stun.l.google.com:19302"
     }]
 };
 properties.pcConstraints = {
    "optional": []
 };

 peerConn = new RTCPeerConnection(properties.pcConfig, properties.pcConstraints);
 peerConn.close();

以下是一些peerConnection属性的输出:

peerConn.signalingState     -->  "closed"
peerConn.iceConnectionState -->  "closed"
peerConn.iceGatheringState  -->  "complete"

你有一个自包含的示例来演示这个问题吗?你的问题听起来像是浏览器中的一个错误,因此在问题跟踪器(https://crbug.com)上报告问题将是比寻找解决方法更好的解决方案。 - Rob W
@RobW - 抱歉回复晚了。是的,我已经在WebRTC问题跟踪器上报告了这个问题,但是这个问题的关键是要从多个角度攻击问题,而不是坐以待毙。 :) https://code.google.com/p/webrtc/issues/detail?id=3717。不幸的是,我没有一个自包含的例子,而且我非常确定Alex的解决方案会解决这个问题。我还加入了更多的错误处理来防止其他客户端相互破坏。作为WebRTC开发人员,我们必须自己做很多事情来防止问题,因为没有办法全局重置PeerCo。 - jamesmortensen
话虽如此,复制很容易。只需获取演示代码并尝试在浏览器之间进行重复调用即可。不要对其友好。假装你是用户。我们都知道用户擅长破坏事物。要不耐烦。连续按3或4次呼叫按钮。也许为了保险起见,可以按8或9次。挂断电话,再打回来,然后快速挂断电话,再打回来。把它变成一个游戏,看看你能多快完成。用户不会像开发人员想象的那样使用东西,这意味着我们必须构建坚固的软件,以承受压力。 :) - jamesmortensen
1个回答

3
您应该能够回滚更改。这只有在角色没有改变时才会发生,即第一次调用的调用者仍然是后续调用的调用者,并且可能不适合您的情况,因为您收到的错误消息与对等连接在发出提供之前接收答案有关(即调用者/被叫方之间存在不匹配)。
请注意,关闭状态是最终状态,关闭对等连接后,它将被删除,因为无法重复使用。
在您的情况下,删除原始对等连接并创建新连接是必须的,但这还不够。您需要重新启动握手,并确保针对原始对等连接的消息不会被其他对等连接捕获和使用。多方客户端也有相同的设计问题。解决它的一种方法,也可以解决闪烁问题,就是在您离线交换的“offer”、“answer”、“candidate”消息中添加“origin”和可能的“target”字段。您必须自己生成ID,因为对等连接对象默认没有唯一的ID。
以下是正常的流程(使用trickle ICE):
  • A上线,创建一个对等连接,并将其存储在索引A_0的映射中
  • B上线,与A类似,使用B_0(确保ID唯一)
  • A呼叫B
  • A发送一个带有额外字段“origin”:“A_0”和“target”:“B_0”的offer
  • B接收到offer,设置本地描述(开始ICE gathering),发送答案,其中包括origin:B_0,target:A_0,完成
  • A接收到答案后,设置本地描述(开始ICE gathering),完成
  • 交换ICE候选项,并适当设置目标和源字段。

现在是问题情况:

  • A在收到B的答案之前删除了它的A_0对等连接。
  • B为A_0发送答案和ICE候选项,但被A丢弃,因为A_0不存在。
  • A重新启动A_1的操作

闪烁情况:

  • A和B同时向对方发送offer
  • 比较ID并根据可重复的任意规则选择一个(更大的数字,更大的字符串,使用字母顺序等)
请注意,此设计还允许多方通话,因为每个通话都将由地图隔离,并由消息逻辑路由。
function handleGenericMsg(msg){
  if(  msg.origin  === username 
    ) {
    trace( 'MAIN - [' + username + '] Ignoring self message: ' + msg.type + '.' );
    return;
  }
  switch(msg.type){
    case 'offer':
      offerHandler(msg);
      break;
    default:
      trace( 'MAIN - [' + targetMid + '] Unsupported message received: ' +     JSON.stringify(msg));
      break;
  }
};

function offerHandler( msg ){
  var targetMid = msg.origin;
  offer = new RTCSessionDescription(msg);
  trace( 'PC   - [' + targetMid + '] Received offer.' )
  var pc = peerConnections[ targetMid ];
  if( !pc ) {
    openPeer( targetMid, false );
    pc = peerConnections[ targetMid ];
  } else {
    // we already had a PC, let's reuse it
  }
  pc.setRemoteDescription( offer );    
  doAnswer( targetMid );
};

我看到你正在使用一个映射来处理peerConnections。这肯定可以防止来自1个对等方的候选者/提议/答案与另一个混淆,但是假设拒绝呼叫的人立即回拨呢?在那种情况下,“origin”是相同的,对吗?如果我理解正确,那么我需要每次添加一个随机字符串来保证每个呼叫的唯一性吗?此外,感谢您提供有关回滚规范的链接! - jamesmortensen
1
注意1:此示例中的命名为username_pccounter。如果您的pccounter跟踪PC的创建/删除,则该对始终是唯一的。 A发送报价。B拒绝报价,发送自己的报价。 - Dr. Alex Gouaillard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接