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

WebRTC: Determine which TURN server is used in PeerConnection

本文关键字:TURN 服务器 确定 PeerConnection WebRTC      更新时间:2023-09-26

场景:您想知道TURN服务器是否被用于特定的调用,以及在PeerConnection创建期间提供的TURN服务器数组中哪一个正在被使用。现在有两个选项:

  • Wireshark:但是当你在一个公司代理和TURN服务器是在外面,Wireshark会显示代理IP作为目的地。(更不用说在后台运行的不便)
  • 浏览统计页面并找出,chrome -> chrome://webrtc-internals和Firefox -> about:webrtc

我想使用上述两个的替代方案,通过编程确定这一点,这样我就不必离开我的应用程序页面。

更新:我已经更新了示例以遵循最新规范,使用mappllike getStats

以下方法遵循规范,目前只适用于Firefox,因为Chrome目前不正确地实现了getStats()。希望,一个版本的适配器.js polyfill应该很快可用,将使这项工作在Chrome以及。

当你在Firefox中运行这段代码时,你会看到:

checking
connected
Does not use TURN

这是因为该示例同时提供了一个STUN和一个TURN服务器。但是当我修改配置仅使用iceTransportPolicy: "relay"的TURN时,我看到:

checking
connected
Uses TURN server: 10.252.73.50

请注意,我使用的转弯服务器是在VPN后面,所以它不会为你工作,但你可以随意修改你自己的服务器(只是不要保存它,除非你想让信息成为公共的!)

虽然我没有使用多个回合服务器进行测试,但正如您可以看到的,显示的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>

我编写并测试了以下代码,适用于最新版本的firefox和chrome, getConnectionDetails返回一个解决连接细节的承诺:

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;
    });
  }
}

我想指出一件事,所有这三种方法在一种情况下都失败了:两个回合服务器从同一台机器在不同的端口上运行,我发现唯一可靠的方法是查看回合服务器日志。