WebRTC:确定PeerConnection中使用的TURN服务器

9
场景:你想知道特定通话是否正在使用TURN服务器以及在PeerConnection创建期间提供的一组TURN服务器中使用哪一个。目前有两个选项:
  • Wireshark:但当您身处公司代理后面且TURN服务器位于其外部时,Wireshark将显示代理IP作为目标IP。(还要考虑在后台运行的不便)
  • 浏览统计页面并查找,Chrome-->chrome://webrtc-internals和Firefox-->about:webrtc。

我想使用另一种方法来程序化地确定这一点,这样我就不必离开我的应用程序页面。


https://github.com/webrtc/apprtc/pull/99 展示了如何确定所使用的TURN服务器类型(udp,tcp,tls)-- 它无法在Firefox中工作,但这主要是apprtc示例稍有滞后的问题。 - Philipp Hancke
2个回答

6

更新:我已经按照最新的规范更新了示例,使用类似于map的getStats

以下方法遵循规范,目前仅在Firefox中有效,因为Chrome目前实现了错误的getStats()。希望adapter.js polyfill的版本能够很快推出,使其在Chrome中也能正常工作。

当您在Firefox中运行这个fiddle时,您会看到:

checking
connected
Does not use TURN

这是因为示例提供了STUN和TURN服务器。但是当我修改配置仅使用TURN并设置iceTransportPolicy: "relay"时,我看到的结果是:
checking
connected
Uses TURN server: 10.252.73.50

请注意,我使用的转发服务器在VPN后面,因此对您来说将无法使用,但是请随意使用您自己的服务器修改fiddle(只要您不保存它,除非您希望信息公开!)
虽然我尚未测试过多个转发服务器,但如您所见,显示的IP地址与配置的转发服务器匹配,因此可以使用此方法确定使用的服务器。

// Turn server is on Mozilla's VPN.
var cfg = { iceTransportPolicy: "all", // set to "relay" to force TURN.
            iceServers: [{ urls: "stun:stun.l.google.com:19302" },
                         { urls: "turn:10.252.73.50",
                           username:"webrtc", credential:"firefox" }] };
var pc1 = new RTCPeerConnection(cfg), pc2 = new RTCPeerConnection(cfg);

pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
pc2.oniceconnectionstatechange = () => log(pc2.iceConnectionState);
pc2.onaddstream = e => v2.srcObject = e.stream;

var findSelected = stats =>
  [...stats.values()].find(s => s.type == "candidate-pair" && s.selected);

var start = () => navigator.mediaDevices.getUserMedia({ video: true })
  .then(stream => pc1.addStream(v1.srcObject = stream))
  .then(() => pc1.createOffer()).then(d => pc1.setLocalDescription(d))
  .then(() => pc2.setRemoteDescription(pc1.localDescription))
  .then(() => pc2.createAnswer()).then(d => pc2.setLocalDescription(d))
  .then(() => pc1.setRemoteDescription(pc2.localDescription))
  .then(() => waitUntil(() => pc1.getStats().then(s => findSelected(s))))
  .then(() => pc1.getStats())
  .then(stats => {
    var candidate = stats.get(findSelected(stats).localCandidateId);
    if (candidate.candidateType == "relayed") {
      log("Uses TURN server: " + candidate.ipAddress);
    } else {
      log("Does not use TURN (uses " + candidate.candidateType + ").");
    }
  })
  .catch(log);

var waitUntil = f => Promise.resolve(f())
  .then(done => done || wait(200).then(() => waitUntil(f)));

var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var log = msg => div.innerHTML += msg +"<br>";
var failed = e => log(e +", line "+ e.lineNumber);
<video id="v1" width="108" height="81" autoplay></video>
<video id="v2" width="108" height="81" autoplay></video><br>
<button onclick="start()">Start!</button><br><div id="div"></div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>


对我来说,问题在于TURN服务器(在EC2实例上运行)中有时会出现某些端口无响应的情况,因此我会在多个端口上运行它们,因此尽管我们可以从rtc统计数据中获取IP地址,但端口仍将是一个谜。 - mido
@mido22 candidate.portNumber 应该返回端口号。请参见这里 - jib
是的,但是该端口号是连接到TURN的对等方的端口号,而不是TURN正在运行的端口号。我通常会查看TURN服务器日志... - mido
1
啊,我明白了。candidate.addressSourceUrl 应该是正确的答案,但不幸的是,在 Firefox 中它还没有被实现。 - jib
candidateType 似乎也已经改为“LocalCandidateType”和“RemoteCandidateType”。我猜测他们已将候选者合并到一个单一的状态中。 - radalin
显示剩余2条评论

4

我编写并测试了下面的代码,在最新版本的Firefox和Chrome中工作正常,getConnectionDetails返回一个解析连接详细信息的Promise:

function getConnectionDetails(peerConnection){


  var connectionDetails = {};   // the final result object.

  if(window.chrome){  // checking if chrome

    var reqFields = [   'googLocalAddress',
                        'googLocalCandidateType',   
                        'googRemoteAddress',
                        'googRemoteCandidateType'
                    ];
    return new Promise(function(resolve, reject){
      peerConnection.getStats(function(stats){
        var filtered = stats.result().filter(function(e){return e.id.indexOf('Conn-audio')==0 && e.stat('googActiveConnection')=='true'})[0];
        if(!filtered) return reject('Something is wrong...');
        reqFields.forEach(function(e){connectionDetails[e.replace('goog', '')] = filtered.stat(e)});
        resolve(connectionDetails);
      });
    });

  }else{  // assuming it is firefox
    return peerConnection.getStats(null).then(function(stats){
        var selectedCandidatePair = stats[Object.keys(stats).filter(function(key){return stats[key].selected})[0]]
          , localICE = stats[selectedCandidatePair.localCandidateId]
          , remoteICE = stats[selectedCandidatePair.remoteCandidateId];
        connectionDetails.LocalAddress = [localICE.ipAddress, localICE.portNumber].join(':');
        connectionDetails.RemoteAddress = [remoteICE.ipAddress, remoteICE.portNumber].join(':');
        connectionDetails.LocalCandidateType = localICE.candidateType;
        connectionDetails.RemoteCandidateType = remoteICE.candidateType;
        return connectionDetails;
    });

  }
}

我想指出一件事情,所有这三种方法在一个场景下会失败:同一台机器上运行两个不同端口的转发服务器,我找到的唯一可靠的方法是查看转发服务器日志。


谢谢!我发现大约25%的时间结果中没有“Conn-audio”条目,尽管ICE连接状态为“已完成”。你有什么想法吗? - Rob Agar
@RobAgar 不确定是什么原因导致了这个问题,试着问问jib或hancke,他们更理解。 - mido

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