40238b00b6
The native WebSocket is in a much better position to do queue management than us. Many callers also failed to notice this part of the API, causing stalls.
273 lines
8.3 KiB
HTML
273 lines
8.3 KiB
HTML
<html>
|
|
|
|
<head>
|
|
<title>WebSockets Latency Test</title>
|
|
<script src="include/util.js"></script>
|
|
<script src="include/webutil.js"></script>
|
|
<script src="include/websock.js"></script>
|
|
<!-- Uncomment to activate firebug lite -->
|
|
<!--
|
|
<script type='text/javascript'
|
|
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
-->
|
|
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
Host: <input id='host' style='width:100'>
|
|
Port: <input id='port' style='width:50'>
|
|
Encrypt: <input id='encrypt' type='checkbox'>
|
|
<br>
|
|
Payload Size: <input id='payload_size' style='width:50'>
|
|
Send Delay (ms): <input id='sendDelay' style='width:50' value="10">
|
|
<input id='connectButton' type='button' value='Start' style='width:100px'
|
|
onclick="connect();">
|
|
|
|
<br><br>
|
|
<table border=1>
|
|
<tr>
|
|
<th align="right">Packets sent:</th>
|
|
<td align="right"><div id='sent'></div></td>
|
|
</tr><tr>
|
|
<th align="right">Packets Received:</th>
|
|
<td align="right"><div id='received'></div></td>
|
|
</tr><tr>
|
|
<th align="right">Average Latency:</th>
|
|
<td align="right"><div id='laverage'></div></td>
|
|
</tr><tr>
|
|
<th align="right">40 Frame Running Average Latency:</th>
|
|
<td align="right"><div id='lrunning'></div></td>
|
|
</tr><tr>
|
|
<th align="right">Minimum Latency:</th>
|
|
<td align="right"><div id='lmin'></div></td>
|
|
</tr><tr>
|
|
<th align="right">Maximum Latency:</th>
|
|
<td align="right"><div id='lmax'></div></td>
|
|
</tr>
|
|
</table>
|
|
|
|
<br>
|
|
Messages:<br>
|
|
<textarea id="messages" style="font-size: 9;" cols=80 rows=10></textarea>
|
|
</body>
|
|
|
|
|
|
<script>
|
|
|
|
var host = null, port = null, sendDelay = 0,
|
|
ws = null, send_ref = null,
|
|
sent, received, latencies, ltotal, laverage, lrunning, lmin, lmax,
|
|
run_length = 40,
|
|
payload_size = 2000, payload,
|
|
msg_cnt = 0, recv_seq = 0, send_seq = 0;
|
|
|
|
Array.prototype.pushStr = function (str) {
|
|
var n = str.length;
|
|
for (var i=0; i < n; i++) {
|
|
this.push(str.charCodeAt(i));
|
|
}
|
|
}
|
|
|
|
|
|
function message(str) {
|
|
console.log(str);
|
|
cell = $D('messages');
|
|
msg_cnt++;
|
|
cell.innerHTML += msg_cnt + ": " + str + "\n";
|
|
cell.scrollTop = cell.scrollHeight;
|
|
}
|
|
|
|
|
|
function add (x,y) {
|
|
return parseInt(x,10)+parseInt(y,10);
|
|
}
|
|
|
|
function recvMsg(data) {
|
|
//console.log(">> check_respond");
|
|
var i, now, arr, first, last, arr, latency;
|
|
|
|
now = (new Date()).getTime(); // Early as possible
|
|
|
|
arr = ws.rQshiftBytes(ws.rQlen());
|
|
first = String.fromCharCode(arr[0]);
|
|
last = String.fromCharCode(arr[arr.length-1]);
|
|
|
|
if (first != "^") {
|
|
message("Error: packet missing start char '^'");
|
|
disconnect();
|
|
return;
|
|
}
|
|
if (last != "$") {
|
|
message("Error: packet missing end char '$'");
|
|
disconnect();
|
|
return;
|
|
}
|
|
text = ''
|
|
for (var i = 1; i < arr.length-1; i++) {
|
|
text += String.fromCharCode(arr[i]);
|
|
}
|
|
arr = text.split(':');
|
|
seq = arr[0];
|
|
timestamp = parseInt(arr[1],10);
|
|
rpayload = arr[2];
|
|
|
|
if (seq != recv_seq) {
|
|
message("Error: expected seq " + recv_seq + " but got " + seq);
|
|
disconnect();
|
|
return;
|
|
}
|
|
recv_seq++;
|
|
if (payload !== rpayload) {
|
|
message("Payload corrupt");
|
|
disconnect();
|
|
return;
|
|
}
|
|
|
|
received++;
|
|
|
|
latency = now - timestamp;
|
|
latencies.push(latency);
|
|
if (latencies.length > run_length) {
|
|
latencies.shift();
|
|
}
|
|
ltotal += latency;
|
|
laverage = ltotal / received;
|
|
lrunning = 0;
|
|
for (var i=0; i < latencies.length; i++) {
|
|
lrunning += latencies[i];
|
|
}
|
|
lrunning = lrunning / latencies.length;
|
|
|
|
if (latency < lmin) {
|
|
lmin = latency;
|
|
}
|
|
if (latency > lmax) {
|
|
lmax = latency;
|
|
}
|
|
|
|
showStats();
|
|
//console.log("<< check_respond");
|
|
}
|
|
|
|
function sendMsg() {
|
|
var arr = [];
|
|
timestamp = (new Date()).getTime();
|
|
arr.pushStr("^" + send_seq + ":" + timestamp + ":" + payload + "$");
|
|
send_seq ++;
|
|
ws.send(arr);
|
|
sent++;
|
|
|
|
showStats();
|
|
send_ref = setTimeout(sendMsg, sendDelay);
|
|
}
|
|
|
|
function showStats() {
|
|
$D('sent').innerHTML = sent;
|
|
$D('received').innerHTML = received;
|
|
$D('laverage').innerHTML = laverage.toFixed(2);
|
|
$D('lrunning').innerHTML = lrunning.toFixed(2);
|
|
$D('lmin').innerHTML = lmin.toFixed(2);
|
|
$D('lmax').innerHTML = lmax.toFixed(2);
|
|
}
|
|
|
|
function init_ws() {
|
|
console.log(">> init_ws");
|
|
var scheme = "ws://";
|
|
if ($D('encrypt').checked) {
|
|
scheme = "wss://";
|
|
}
|
|
var uri = scheme + host + ":" + port;
|
|
console.log("connecting to " + uri);
|
|
ws = new Websock();
|
|
ws.maxBufferedAmount = 5000;
|
|
ws.open(uri);
|
|
|
|
ws.on('message', function() {
|
|
recvMsg();
|
|
});
|
|
ws.on('open', function() {
|
|
send_ref = setTimeout(sendMsg, sendDelay);
|
|
});
|
|
ws.on('close', function(e) {
|
|
disconnect();
|
|
});
|
|
ws.on('error', function(e) {
|
|
message("Websock error: " + e);
|
|
disconnect();
|
|
});
|
|
|
|
console.log("<< init_ws");
|
|
}
|
|
|
|
function connect() {
|
|
console.log(">> connect");
|
|
host = $D('host').value;
|
|
port = $D('port').value;
|
|
payload_size = parseInt($D('payload_size').value, 10);
|
|
sendDelay = parseInt($D('sendDelay').value, 10);
|
|
|
|
if ((!host) || (!port)) {
|
|
console.log("must set host and port");
|
|
return;
|
|
}
|
|
|
|
if (ws) {
|
|
ws.close();
|
|
}
|
|
init_ws();
|
|
|
|
// Populate payload data
|
|
var numlist = []
|
|
for (var i=0; i < payload_size; i++) {
|
|
numlist.push( Math.floor(Math.random()*10) );
|
|
}
|
|
payload = numlist.join('');
|
|
|
|
// Initialize stats
|
|
sent = 0;
|
|
received = 0;
|
|
latencies = [];
|
|
ltotal = 0;
|
|
laverage = 0;
|
|
lrunning = 0;
|
|
lmin = 999999999;
|
|
lmax = 0;
|
|
|
|
$D('connectButton').value = "Stop";
|
|
$D('connectButton').onclick = disconnect;
|
|
console.log("<< connect");
|
|
}
|
|
|
|
function disconnect() {
|
|
console.log(">> disconnect");
|
|
if (ws) {
|
|
ws.close();
|
|
}
|
|
|
|
if (send_ref) {
|
|
clearInterval(send_ref);
|
|
send_ref = null;
|
|
}
|
|
showStats(); // Final numbers
|
|
recv_seq = 0;
|
|
send_seq = 0;
|
|
|
|
$D('connectButton').value = "Start";
|
|
$D('connectButton').onclick = connect;
|
|
console.log("<< disconnect");
|
|
}
|
|
|
|
|
|
window.onload = function() {
|
|
console.log("onload");
|
|
var url = document.location.href;
|
|
$D('host').value = (url.match(/host=([^&#]*)/) || ['',window.location.hostname])[1];
|
|
$D('port').value = (url.match(/port=([^&#]*)/) || ['',window.location.port])[1];
|
|
$D('payload_size').value = payload_size;
|
|
}
|
|
</script>
|
|
|
|
</html>
|