STUN/TURN 服务器连接测试

Posted

技术标签:

【中文标题】STUN/TURN 服务器连接测试【英文标题】:STUN/TURN server connectivity test 【发布时间】:2015-04-30 14:04:49 【问题描述】:

我试图弄清楚如何测试 STUN/TURN 服务器是否处于活动状态并正确响应连接。理想情况下,此测试应从外部机器执行,以防 STUN/TURN 机器因这种情况而关闭,连接测试也应报告。

过去有没有人调查过这个案例?会推荐哪些解决方案?

【问题讨论】:

您可以尝试从中收集 ICE 候选人。 【参考方案1】:

编辑: github.io 中的一个不错的实现,取自对另一个答案的评论(在 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(
    urls: '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));

你可以运行下面的sn-p来检查:

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>

【讨论】:

收集候选者的输出应该是什么,以确保 stun 或 turn 服务器正常工作。 webrtc.github.io/samples/src/content/peerconnection/trickle-ice 请勿将test 用作用户名,如示例所示。显然有一些test 用户已经在浏览器的幕后实现,因此它被烧毁了,如果你尝试它进行测试,事情会无缘无故地失败。不过,我没有找到关于这种行为的文档,但是在使用了不同的名称后,一切都突然开始工作了.. @mido 你能在这里回答我的问题吗-> ***.com/questions/60946697/…【参考方案2】:

您可以在这里测试您的服务器...

https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/

【讨论】:

这太棒了!有没有办法将其作为内部解决方案实施?我想要一个守护进程来定期检查服务器是否处于活动状态,而不是手动执行。 可以看代码here 强烈建议从 github 获取最新版本,由 @gugaiz 链接。 temasys.com 链接似乎已过时,在 Firefox 中对我不起作用。 成功的测试结果应该显示什么?很抱歉被他们在页面上的说明弄糊涂了 @mirazour 检查我的答案以自动检查服务器【参考方案3】:

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

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

仍然测试 TURN 服务器的一种方法是安装 turnutils_uclientsudo 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 中镜像。

【讨论】:

还有用于stun的turnutils_stunclient:turnutils_stunclient -p 3478 stunserver.com 在不阅读 RfC 的情况下,似乎可以根据当前时间和静态密码计算出一个临时有效的用户名:secret=$(awk -F "=" '/^static-auth-secret/ print $2' /etc/turnserver.conf) &amp;&amp; time=$(date +%s) &amp;&amp; expiry=3600 &amp;&amp; username=$(( $time + $expiry )) &amp;&amp; echo username:$username &amp;&amp; echo "password : $(echo -n $username | openssl dgst -binary -sha1 -hmac $secret | openssl base64)" 感谢blog.wydler.eu/2020/04/04/… 提供此脚本。 (调整为使用 conf 中已注释掉的 static-auth-secret 行) Jupp,BigBlueButton 也为此发布了script。它允许您使用在另一个答案中发布的Trickle ICE。【参考方案4】:

如果你想经常检查 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; 监听端口=20000; # 如果此端口不可用,请随意更改此端口

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

【讨论】:

【参考方案5】:

测试你的 TURN 和 STUN 服务器here

【讨论】:

我可以在那里测试turn server,但我不确定stun server是否工作正常,你能建议任何方法来测试stun server【参考方案6】:

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

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 prflx')!=-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));

【讨论】:

即使stun服务器离线也显示你已经打开服务器,但我没有 @waza123 如果转向服务器不可用,请说错误的凭据,如果离线,它会恢复为“作为 STUN 活动”,它会说:“不,转向服务器不活动”【参考方案7】:

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】:

这是@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】:

您可以设置第 3 方监控服务(我们使用 Monitis)甚至您自己的机器,以每分钟从 1 个或多个位置 PING 服务器。但是,这只会告诉您服务器是否可访问,而不一定告诉您 TURN/STUN 应用程序服务器是否仍接受并响应 TURN/STUN 数据包。

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

【讨论】:

以上是关于STUN/TURN 服务器连接测试的主要内容,如果未能解决你的问题,请参考以下文章

coTurn测试程序之turnutils_stunclient.exe

stun/turn服务器部署

STUN-TURN 服务器端口范围风险

WebRTC - 我需要指定多少 STUN/TURN 服务器?

为 WebRTC 应用程序实现我们自己的 STUN/TURN 服务器 [重复]

STUN, TURN, ICE介绍