| <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=([^&#]*)/) || ['',window.location.hostname])[1]; |
| $D('port').value = (url.match(/port=([^&#]*)/) || ['',window.location.port])[1]; |
| $D('payload_size').value = payload_size; |
| } |
| </script> |
| |
| </html> |