STUN / TURN服务器连接测试

时间:2015-02-27 18:50:58

标签: webrtc stun turn

我试图弄清楚如何测试STUN / TURN服务器是否存活并正确响应连接。理想情况下,此测试将在外部机器上执行,以防万一STUN / TURN机器因此情况下降,也应通过连接测试报告。

过去有没有人调查此案?建议使用哪些解决方案?

10 个答案:

答案 0 :(得分:30)

编辑: github.io中一个很好的实现,从评论到另一个答案(选择" relay"在IceTransports值中):

Test TURN Server

根据Benjamin Trent的建议,我编写了以下代码来测试TURN服务器的连接,适用于firefox n chrome:

function checkTURNServer(turnConfig, timeout){ 

  return new Promise(function(resolve, reject){

    setTimeout(function(){
        if(promiseResolved) return;
        resolve(false);
        promiseResolved = true;
    }, timeout || 5000);

    var promiseResolved = false
      , myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection   //compatibility for firefox and chrome
      , pc = new myPeerConnection({iceServers:[turnConfig]})
      , noop = function(){};
    pc.createDataChannel("");    //create a bogus data channel
    pc.createOffer(function(sdp){
      if(sdp.sdp.indexOf('typ relay') > -1){ // sometimes sdp contains the ice candidates...
        promiseResolved = true;
        resolve(true);
      }
      pc.setLocalDescription(sdp, noop, noop);
    }, noop);    // create offer and set local description
    pc.onicecandidate = function(ice){  //listen for candidate events
      if(promiseResolved || !ice || !ice.candidate || !ice.candidate.candidate || !(ice.candidate.candidate.indexOf('typ relay')>-1))  return;
      promiseResolved = true;
      resolve(true);
    };
  });   
}

示例用法:

checkTURNServer({
    url: 'turn:127.0.0.1',
    username: 'test',
    credential: 'test'
}).then(function(bool){
    console.log('is TURN server active? ', bool? 'yes':'no');
}).catch(console.error.bind(console));

您可以运行以下代码段进行检查:



var res = id('result');

id('button').onclick = function(){
	res.innerHTML = 'Checking TURN Server...';
  var url = 'turn:'+id('url').value+':'+id('port').value,
  		useUDP = id('udp').checked;
  url +='?transport=' + (useUDP ? 'udp': 'tcp');
  checkTURNServer({
      urls: url,
      username: id('name').value, 
      credential: id('pass').value
  }, id('time').value).then(function(bool){
  		if(bool)
         res.innerHTML = 'Yep, the TURN server works...';
      else
         throw new Error('Doesn\'t work');
  }).catch(function(e){
  	 console.log(e);
     res.innerHTML = 'TURN server does not work.';
  });
};


function checkTURNServer(turnConfig, timeout){ 
	console.log('turnConfig: ', turnConfig);
  return new Promise(function(resolve, reject){

    setTimeout(function(){
        if(promiseResolved) return;
        resolve(false);
        promiseResolved = true;
    }, timeout || 5000);

    var promiseResolved = false
      , myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection   //compatibility for firefox and chrome
      , pc = new myPeerConnection({iceServers:[turnConfig]})
      , noop = function(){};
    pc.createDataChannel("");    //create a bogus data channel
    pc.createOffer(function(sdp){
      if(sdp.sdp.indexOf('typ relay') > -1){ // sometimes sdp contains the ice candidates...
        promiseResolved = true;
        resolve(true);
      }
      pc.setLocalDescription(sdp, noop, noop);
    }, noop);    // create offer and set local description
    pc.onicecandidate = function(ice){  //listen for candidate events
      if(promiseResolved || !ice || !ice.candidate || !ice.candidate.candidate || !(ice.candidate.candidate.indexOf('typ relay')>-1))  return;
      promiseResolved = true;
      resolve(true);
    };
  });   
}


function id(val){
	return document.getElementById(val);
}

#url{
  width: 250px;
}
#port{
  width: 70px;
}

<h1>
 Test TURN server
</h1>
<div>
TURN URL: <input id='url' placeholder='example.com  or  xxx.yyy.rrr.ddd'  />
Port: <input type='number' value='3478' id='port' placeholder='enter a port number' />
</div>
<div>
Transport: <input type="radio" name="transport" id="tcp" value="tcp" /> TCP
<input type="radio" name="transport" id="udp" value="udp" checked/>UDP
</div>

<div>
Username: <input id="name" placeholder="turn username" />
</div>
<div>
password: <input id="pass" placeholder="turn password" />
</div>

<div>
checking Timeout: <input type='number'  id="time" placeholder="wait time  before checking timeout" value=5000 />
</div>
<div>
<button id='button'>
Check TURN Server
</button>
</div>

<h4 id='result'></h4>
&#13;
&#13;
&#13;

答案 1 :(得分:6)

答案 2 :(得分:4)

测试您的TURN和STUN服务器here

答案 3 :(得分:3)

如果您为Coturn服务器使用 username:password 身份验证机制,则较早的答案适用于TURN。但是,就像static-auth-secret中看到的BigBlueButton和其他使用/etc/turnserver.conf的情况一样,不可能使用https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/

use-auth-secret
static-auth-secret=XXXX

仍然要测试TURN服务器的一种方法是使用turnutils_uclient或您各自的软件包管理器安装sudo apt install coturn。然后,您可以使用(代替XXXX和turn.example.com)对其进行测试:

turnutils_uclient -T -W XXXX turn.example.com

这将导致以下输出(已编辑的IP地址192.168.0.2作为内部客户端地址,而1.2.3.4作为服务器地址):

0: IPv4. Connected from: 192.168.0.2:50988
0: IPv4. Connected to: 1.2.3.4:3478
0: allocate sent
0: allocate response received: 
0: allocate sent
0: allocate response received: 
0: success
0: IPv4. Received relay addr: 1.2.3.4:56365
....
4: Total transmit time is 4
4: Total lost packets 0 (0.000000%), total send dropped 0 (0.000000%)
4: Average round trip delay 32.500000 ms; min = 15 ms, max = 56 ms
4: Average jitter 12.600000 ms; min = 0 ms, max = 41 ms

在您的TURN服务器上,这应该反映在/var/log/coturn.log中。

答案 4 :(得分:0)

您可以设置第三方监控服务(我们使用Monitis)甚至您自己的计算机,每隔一分钟从一个或多个位置 PING 服务器。但是,这只会告诉您服务器是否 可以 ,并且如果TURN / STUN应用程序服务器仍然接受&amp;响应TURN / STUN数据包。

STUN / TURN的服务器端监控库将成为一个很棒的GitHub项目。

答案 5 :(得分:0)

如果您愿意使用商业工具对100,000个虚拟用户或1百万个虚拟用户之类的转弯服务器进行高负载测试,则可能需要检查Load Multiplier工具(www.loadmultiplier.com)。 here还介绍了如何测试Coturn服务器的特定页面。

请注意,任一负载乘数工具都会模拟一个虚拟信令平面(以在客户端之间交换SDP)。您还可以使用SRTP / DTLS媒体测试STUN,TURN和ICE。

答案 6 :(得分:0)

@mido函数的版本,用于同时检查TURN和STUN服务器(原始拒绝stun服务器):

function checkTurnOrStun(turnConfig, timeout){ 
  return new Promise(function(resolve, reject){

    setTimeout(function(){
        if(promiseResolved){
            if (promiseResolved == 'STUN') resolve('STUN');
            return;
        }
        resolve(false);
        promiseResolved = true;
    }, timeout || 5000);

    var promiseResolved = false
      , myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection   //compatibility for firefox and chrome
      , pc = new myPeerConnection({iceServers:[turnConfig]})
      , noop = function(){};
    pc.createDataChannel("");    //create a bogus data channel
    pc.createOffer(function(sdp){
      if(sdp.sdp.indexOf('typ relay') > -1){ // sometimes sdp contains the ice candidates...
        promiseResolved = 'TURN'; 
        resolve(true);
      }
      pc.setLocalDescription(sdp, noop, noop);
    }, noop);    // create offer and set local description
    pc.onicecandidate = function(ice){  //listen for candidate events
      if( !ice || !ice.candidate || !ice.candidate.candidate)  return;
      if (ice.candidate.candidate.indexOf('typ relay')!=-1) { promiseResolved = 'TURN'; resolve('TURN'); }
      else if (!promiseResolved && (ice.candidate.candidate.indexOf('typ host')!=-1 || ice.candidate.candidate.indexOf('typ srflx')!=-1)){
          promiseResolved = 'STUN';
        if (turnConfig.url.indexOf('turn:')!==0) resolve('STUN');
      }
      else return;
    };
  });   
}

checkTurnOrStun({"url": "stun:stunserver.org"}).then(function(result){
    console.log(
    result ? 'YES, Server active as '+result : 'NO, server not active');
}).catch(console.error.bind(console));

checkTurnOrStun({
            url: 'turn:numb.viagenie.ca',
            credential: 'muazkh',
            username: 'webrtc@live.com'
}).then(function(result){
    console.log(
    result ? 'YES, Server active as '+result : 'NO, server not active');
}).catch(console.error.bind(console));

答案 7 :(得分:0)

npm我惊呆了

const stun = require('stun');

stun.request('stun.l.google.com:19302', (err, res) => {
  if (err) {
    console.error(err);
  } else {
    const { address } = res.getXorAddress();
    console.log('your ip', address);
  }
});

答案 8 :(得分:0)

这里是@mido答案的清理和更新版本,用于检查服务器是否可访问。这对于在线测试失败的专用网络特别有用。

它还将候选人的类型打印到控制台。

功能

const checkTURNServer = (turnConfig, timeout = 5000) => {
    return new Promise(async (resolve, reject) => {
        const pc = new RTCPeerConnection({iceServers: [turnConfig]});
        let promiseResolved = false;
        // Stop waiting after X milliseconds and display the result
        setTimeout(() => {
            if(promiseResolved)
                return;
            promiseResolved = true;
            resolve(false);
        }, timeout);
        // Create a bogus data channel
        pc.createDataChannel('');
        // Listen for candidates
        pc.onicecandidate = ice => {
            if(promiseResolved || ice === null || ice.candidate === null)
                return;
            console.log(ice.candidate.type);
            if(ice.candidate.type === 'relay') {
                promiseResolved = true;
                resolve(true);
            }
        };
        // Create offer and set local description
        const offer = await pc.createOffer();
        await pc.setLocalDescription(offer);
    });
};

用法

checkTURNServer({
    urls: ['turn:' + location.host + ':3478', 'turns:' + location.host + ':5349'],
    username: "1604441890:myUser",
    credential: "myPassword",
    credentialType: 'password'
}).then(
    active => console.log('Is the TURN server active?', active ? 'Yes :)' : 'Not yet...keep trying ;)')
).catch(
    e => console.error(e)
);

答案 9 :(得分:-1)

如果要不断检查stun服务器,可以使用cron执行此命令:

stunserver=stun1.l.google.com;stunport=19302;listenport=20000;echo -ne "\x00\x01\x00\x00YOGO\x59\x4f\x47\x4fSTACFLOW" | nc -u -p $listenport $stunserver $stunport -w 0;timeout 1 nc -l -u $listenport | head -c 32 | tail -c 4 | hexdump -e '/1 "%u" "."' | grep -o ".*[^.]" && echo yes-no-problem || mail -s "Error in Tun server:$stunserver:$stunport" root@localhost <<< 'Error in Tun server'

用您的电子邮件替换root @ localhost以获取报告。

stunserver = stun1.l.google.com;
stunport = 19302;
listenport = 20000; #自由更改此端口(如果不可用)

将其添加到cron并每分钟执行一次。