我试图弄清楚如何测试 STUN/TURN 服务器是否处于活动状态并正确响应连接。理想情况下,该测试将从外部机器执行,以防 STUN/TURN 机器因这种情况而关闭,连接测试也应报告。
过去有人调查过这个案子吗?会推荐哪些解决方案?
编辑: github.io 中的一个很好的实现从评论到另一个答案(在 IceTransports 值中选择“中继”):
按照 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));
您可以运行以下代码段进行检查:
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>
如果您对 coturn 服务器使用用户名:密码身份验证机制,则较早的答案适用于 TURN。但是,就像 BigBlueButton 和其他使用的情况static-auth-secret
一样/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
.
如果你想经常检查 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;
眩晕= 19302;
监听端口=20000;# 如果此端口不可用,请自由更改此端口
将其添加到 cron 并每分钟执行一次。
在这里测试你的 TURN 和 STUN 服务器
用于检查 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));
这是@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)
);
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);
}
});
您可以设置第 3 方监控服务(我们使用 Monitis),甚至您自己的机器每分钟从 1 个或多个位置PING服务器。但是,这只会告诉您服务器是否可访问,而不一定告诉您 TURN/STUN 应用程序服务器是否仍然接受并响应 TURN/STUN 数据包。
用于 STUN/TURN 的服务器端监控库将是一个很棒的 GitHub 项目。