88b71ce171
- Add initial IETF-07 (HyBi-07) protocol version support. This version still uses base64 encoding since the API for binary support is not yet finalized. - Move socket send and recieve functions into the WebSocketServer class instead of having the sub-class do this. This simplifies sub-classes somewhat. The send_frame routine now returns the number of frames that were unable to be sent. If this value is non-zero then the sub-class should call again when the socket is ready until the pending frames count is 0. - Do traffic reporting in the main class instead. - When the client is HyBi style (i.e. IETF-07) then use the sub-protocol header to select whether to do base64 encoding or simply send the frame data raw (binary). Update include/websock.js to send a 'base64' protocol selector. Once the API support binary, then the client will need to detect this and set the protocol to 'binary'.
291 lines
9.0 KiB
HTML
291 lines
9.0 KiB
HTML
<html>
|
|
|
|
<head>
|
|
<title>WebSockets Latency Test</title>
|
|
<script src="include/base64.js"></script>
|
|
<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, actualSendDelay,
|
|
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.shift());
|
|
last = String.fromCharCode(arr.pop());
|
|
|
|
if (first != "^") {
|
|
message("Error: packet missing start char '^'");
|
|
disconnect();
|
|
return;
|
|
}
|
|
if (last != "$") {
|
|
message("Error: packet missing end char '$'");
|
|
disconnect();
|
|
return;
|
|
}
|
|
arr = arr.map(function(num) {
|
|
return String.fromCharCode(num);
|
|
} ).join('').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 = [];
|
|
if (! ws.flush() ) {
|
|
message("WebSocket not ready, backing off");
|
|
actualSendDelay = actualSendDelay * 2;
|
|
send_ref = setTimeout(sendMsg, actualSendDelay);
|
|
return false;
|
|
} else {
|
|
// Scale the delay down to the requested minimum
|
|
if (actualSendDelay > sendDelay) {
|
|
message("WebSocket ready, increasing presure");
|
|
actualSendDelay = Math.max(actualSendDelay / 2, sendDelay);
|
|
}
|
|
}
|
|
|
|
timestamp = (new Date()).getTime();
|
|
arr.pushStr("^" + send_seq + ":" + timestamp + ":" + payload + "$");
|
|
send_seq ++;
|
|
ws.send(arr);
|
|
sent++;
|
|
|
|
showStats();
|
|
send_ref = setTimeout(sendMsg, actualSendDelay);
|
|
}
|
|
|
|
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;
|
|
actualSendDelay = sendDelay;
|
|
|
|
$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");
|
|
if (Websock_native) {
|
|
message("Using native WebSockets");
|
|
} else {
|
|
message("initializing web-socket-js flash bridge");
|
|
}
|
|
var url = document.location.href;
|
|
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
|
|
$D('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
|
|
$D('payload_size').value = payload_size;
|
|
}
|
|
</script>
|
|
|
|
</html>
|