Use websock.js in latency test. Fixes for Opera.
Convert latency test to use include/websock.js instead of direct WebSockets handling. Add support for configuring the maximum bufferedAmount. This allows us to configure it high enough to get around a bug in bufferedAmount reporting in Opera. Add some latency test results for Opera 11 with WebSockets turned on.
This commit is contained in:
parent
307dda1ad0
commit
3d91fd3492
|
@ -1,7 +1,5 @@
|
|||
- wstelnet: support CSI L and CSI M
|
||||
|
||||
- mod_websockify module for Apache
|
||||
|
||||
- create gevent version:
|
||||
http://nichol.as/benchmark-of-python-web-servers
|
||||
http://n01se.net/paste/QXO
|
||||
|
|
|
@ -16,6 +16,14 @@ firefox 4.0b9 - WebSockets enabled
|
|||
Minimum Latency: 5.00
|
||||
Maximum Latency: 119.00
|
||||
|
||||
Opera 11 - WebSockets enabled
|
||||
Packets sent: 3065
|
||||
Packets Received: 3064
|
||||
Average Latency: 9.56
|
||||
40 Frame Running Average Latency: 8.15
|
||||
Minimum Latency: 4.00
|
||||
Maximum Latency: 53.00
|
||||
|
||||
---
|
||||
|
||||
firefox 4.0b9 - no WebSockets
|
||||
|
|
|
@ -169,7 +169,7 @@ function flush() {
|
|||
if (websocket.bufferedAmount !== 0) {
|
||||
Util.Debug("bufferedAmount: " + websocket.bufferedAmount);
|
||||
}
|
||||
if (websocket.bufferedAmount < 1000) {
|
||||
if (websocket.bufferedAmount < api.maxBufferedAmount) {
|
||||
//Util.Debug("arr: " + arr);
|
||||
//Util.Debug("sQ: " + sQ);
|
||||
if (sQ) {
|
||||
|
@ -281,6 +281,9 @@ function close() {
|
|||
}
|
||||
|
||||
function constructor() {
|
||||
// Configuration settings
|
||||
api.maxBufferedAmount = 200;
|
||||
|
||||
// Direct access to send and receive queues
|
||||
api.get_sQ = get_sQ;
|
||||
api.get_rQ = get_rQ;
|
||||
|
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* WebSockets IRC client
|
||||
* Copyright (C) 2011 Joel Martin
|
||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||
*
|
||||
* Includes VT100.js from:
|
||||
* http://code.google.com/p/sshconsole
|
||||
* Which was modified from:
|
||||
* http://fzort.org/bi/o.php#vt100_js
|
||||
|
||||
* IRC Client protocol:
|
||||
* http://www.faqs.org/rfcs/rfc2812.html
|
||||
*/
|
||||
|
||||
|
||||
function IRC(target, connect_callback, disconnect_callback) {
|
||||
|
||||
var that = {}, // Public API interface
|
||||
vt100, ws, sQ = [],
|
||||
state = "unconnected",
|
||||
irc_nick, irc_channel,
|
||||
termType = "VT100";
|
||||
|
||||
|
||||
Array.prototype.pushStr = function (str) {
|
||||
var n = str.length;
|
||||
for (var i=0; i < n; i++) {
|
||||
this.push(str.charCodeAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
function do_send() {
|
||||
if (sQ.length > 0) {
|
||||
Util.Debug("Sending " + sQ);
|
||||
ws.send(sQ);
|
||||
sQ = [];
|
||||
}
|
||||
}
|
||||
|
||||
function do_recv() {
|
||||
console.log(">> do_recv");
|
||||
var rQ, rQi, i;
|
||||
|
||||
while (ws.rQlen() > 1) {
|
||||
rQ = ws.get_rQ();
|
||||
rQi = ws.get_rQi();
|
||||
for (i = rQi; i < rQ.length; i++) {
|
||||
if (rQ[i] === 10) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= rQ.length) {
|
||||
// No line break found
|
||||
break;
|
||||
}
|
||||
recvMsg(ws.rQshiftStr((i-rQi) + 1));
|
||||
}
|
||||
//console.log("<< do_recv");
|
||||
}
|
||||
|
||||
// Handle an IRC message
|
||||
function recvMsg(msg) {
|
||||
Util.Debug(">> recvMsg('" + msg + "')");
|
||||
|
||||
var tokens = msg.split(' '), in_params = true,
|
||||
prefix, command, params = [], trailing = [];
|
||||
|
||||
Util.Info(" tokens: " + tokens);
|
||||
|
||||
if (tokens[0].charAt(0) === ":") {
|
||||
prefix = tokens.shift();
|
||||
}
|
||||
|
||||
command = tokens.shift();
|
||||
|
||||
while (tokens.length > 0) {
|
||||
if (tokens[0].charAt(0) === ":") {
|
||||
in_params = false;
|
||||
}
|
||||
if (in_params) {
|
||||
params.push(tokens.shift());
|
||||
} else {
|
||||
trailing.push(tokens.shift());
|
||||
}
|
||||
}
|
||||
|
||||
Util.Info(" prefix: " + prefix);
|
||||
Util.Info(" command: " + command);
|
||||
Util.Info(" params: " + params);
|
||||
Util.Info(" trailing: " + trailing);
|
||||
|
||||
// Show raw received
|
||||
vt100.write(msg);
|
||||
|
||||
switch (command) {
|
||||
case "004":
|
||||
state = "registered";
|
||||
vt100.write("Joining channel #" + irc_channel);
|
||||
sendCmd("JOIN #" + irc_channel);
|
||||
break;
|
||||
case "JOIN":
|
||||
state = "joined";
|
||||
vt100.write("Joined channel #" + irc_channel);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
Util.Debug("<< recvMsg('" + msg + "')");
|
||||
}
|
||||
|
||||
function sendCmd(msg) {
|
||||
Util.Info("Sending: " + msg);
|
||||
sQ.pushStr(msg + "\r\n");
|
||||
do_send();
|
||||
}
|
||||
|
||||
that.sendMsg = function(msg) {
|
||||
// TODO parse into message
|
||||
sendCmd("PRIVMSG #" + irc_channel + " :" + msg);
|
||||
}
|
||||
|
||||
|
||||
that.connect = function(host, port, encrypt, nick, channel) {
|
||||
var host = host,
|
||||
port = port,
|
||||
scheme = "ws://", uri;
|
||||
|
||||
irc_nick = nick;
|
||||
irc_channel = channel;
|
||||
|
||||
Util.Debug(">> connect");
|
||||
if ((!host) || (!port)) {
|
||||
alert("must set host and port");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ws) {
|
||||
ws.close();
|
||||
}
|
||||
|
||||
if (encrypt) {
|
||||
scheme = "wss://";
|
||||
}
|
||||
uri = scheme + host + ":" + port;
|
||||
Util.Info("connecting to " + uri);
|
||||
|
||||
ws.open(uri);
|
||||
|
||||
Util.Debug("<< connect");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
that.disconnect = function() {
|
||||
Util.Debug(">> disconnect");
|
||||
if (ws) {
|
||||
ws.close();
|
||||
}
|
||||
|
||||
disconnect_callback();
|
||||
Util.Debug("<< disconnect");
|
||||
}
|
||||
|
||||
|
||||
function constructor() {
|
||||
/* Initialize Websock object */
|
||||
ws = new Websock();
|
||||
|
||||
ws.on('message', do_recv);
|
||||
ws.on('open', function(e) {
|
||||
Util.Info(">> WebSockets.onopen");
|
||||
// Send registration commands
|
||||
state = "connected";
|
||||
sendCmd("NICK " + irc_nick);
|
||||
// TODO: how to determine this?
|
||||
sendCmd("USER joelm 0 * :Joel Martin");
|
||||
connect_callback();
|
||||
Util.Info("<< WebSockets.onopen");
|
||||
});
|
||||
ws.on('close', function(e) {
|
||||
Util.Info(">> WebSockets.onclose");
|
||||
that.disconnect();
|
||||
Util.Info("<< WebSockets.onclose");
|
||||
});
|
||||
ws.on('error', function(e) {
|
||||
Util.Info(">> WebSockets.onerror");
|
||||
that.disconnect();
|
||||
Util.Info("<< WebSockets.onerror");
|
||||
});
|
||||
|
||||
/* Initialize the terminal emulator/renderer */
|
||||
|
||||
vt100 = new VT100(80, 24, target);
|
||||
|
||||
// Show cursor
|
||||
vt100.curs_set(true, false);
|
||||
|
||||
/*
|
||||
* Override VT100 I/O routines
|
||||
*/
|
||||
|
||||
// Set handler for sending characters
|
||||
vt100.getch(
|
||||
function send_chr(chr, vt) {
|
||||
var i;
|
||||
Util.Debug(">> send_chr: " + chr);
|
||||
for (i = 0; i < chr.length; i++) {
|
||||
sQ.push(chr.charCodeAt(i));
|
||||
}
|
||||
do_send();
|
||||
vt100.getch(send_chr);
|
||||
}
|
||||
);
|
||||
|
||||
vt100.debug = function(message) {
|
||||
Util.Debug(message + "\n");
|
||||
}
|
||||
|
||||
vt100.warn = function(message) {
|
||||
Util.Warn(message + "\n");
|
||||
}
|
||||
|
||||
vt100.curs_set = function(vis, grab, eventist)
|
||||
{
|
||||
this.debug("curs_set:: vis: " + vis + ", grab: " + grab);
|
||||
if (vis !== undefined)
|
||||
this.cursor_vis_ = (vis > 0);
|
||||
}
|
||||
|
||||
return that;
|
||||
}
|
||||
|
||||
return constructor(); // Return the public API interface
|
||||
|
||||
} // End of Telnet()
|
|
@ -5,6 +5,7 @@
|
|||
<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'
|
||||
|
@ -56,7 +57,7 @@
|
|||
|
||||
<script>
|
||||
|
||||
var host = null, port = null, sendDelay = 0,
|
||||
var host = null, port = null, sendDelay = 0, actualSendDelay,
|
||||
ws = null, send_ref = null,
|
||||
sent, received, latencies, ltotal, laverage, lrunning, lmin, lmax,
|
||||
run_length = 40,
|
||||
|
@ -81,17 +82,18 @@
|
|||
|
||||
|
||||
function add (x,y) {
|
||||
return parseInt(x,10)+parseInt(y,10); }
|
||||
return parseInt(x,10)+parseInt(y,10);
|
||||
}
|
||||
|
||||
function recvMsg(data) {
|
||||
//console.log(">> check_respond");
|
||||
var i, now, decoded, first, last, arr, latency;
|
||||
var i, now, arr, first, last, arr, latency;
|
||||
|
||||
now = (new Date()).getTime(); // Early as possible
|
||||
|
||||
decoded = Base64.decode(data);
|
||||
first = String.fromCharCode(decoded.shift());
|
||||
last = String.fromCharCode(decoded.pop());
|
||||
arr = ws.rQshiftBytes(ws.rQlen());
|
||||
first = String.fromCharCode(arr.shift());
|
||||
last = String.fromCharCode(arr.pop());
|
||||
|
||||
if (first != "^") {
|
||||
message("Error: packet missing start char '^'");
|
||||
|
@ -103,7 +105,7 @@
|
|||
disconnect();
|
||||
return;
|
||||
}
|
||||
arr = decoded.map(function(num) {
|
||||
arr = arr.map(function(num) {
|
||||
return String.fromCharCode(num);
|
||||
} ).join('').split(':');
|
||||
seq = arr[0];
|
||||
|
@ -150,18 +152,27 @@
|
|||
|
||||
function sendMsg() {
|
||||
var arr = [];
|
||||
if (ws.bufferedAmount > 0) {
|
||||
console.log("Delaying send");
|
||||
return;
|
||||
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(Base64.encode(arr));
|
||||
ws.send(arr);
|
||||
sent++;
|
||||
|
||||
showStats();
|
||||
send_ref = setTimeout(sendMsg, actualSendDelay);
|
||||
}
|
||||
|
||||
function showStats() {
|
||||
|
@ -181,28 +192,23 @@
|
|||
}
|
||||
var uri = scheme + host + ":" + port;
|
||||
console.log("connecting to " + uri);
|
||||
ws = new WebSocket(uri);
|
||||
ws = new Websock();
|
||||
ws.maxBufferedAmount = 5000;
|
||||
ws.open(uri);
|
||||
|
||||
ws.onmessage = function(e) {
|
||||
//console.log(">> WebSockets.onmessage");
|
||||
recvMsg(e.data);
|
||||
//console.log("<< WebSockets.onmessage");
|
||||
};
|
||||
ws.onopen = function(e) {
|
||||
console.log(">> WebSockets.onopen");
|
||||
send_ref = setInterval(sendMsg, sendDelay);
|
||||
console.log("<< WebSockets.onopen");
|
||||
};
|
||||
ws.onclose = function(e) {
|
||||
console.log(">> WebSockets.onclose");
|
||||
clearInterval(send_ref);
|
||||
console.log("<< WebSockets.onclose");
|
||||
};
|
||||
ws.onerror = function(e) {
|
||||
console.log(">> WebSockets.onerror");
|
||||
console.log(" " + e);
|
||||
console.log("<< WebSockets.onerror");
|
||||
};
|
||||
ws.on('message', function(e) {
|
||||
recvMsg();
|
||||
});
|
||||
ws.on('open', function(e) {
|
||||
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");
|
||||
}
|
||||
|
@ -240,6 +246,7 @@
|
|||
lrunning = 0;
|
||||
lmin = 999999999;
|
||||
lmax = 0;
|
||||
actualSendDelay = sendDelay;
|
||||
|
||||
$D('connectButton').value = "Stop";
|
||||
$D('connectButton').onclick = disconnect;
|
||||
|
@ -252,8 +259,10 @@
|
|||
ws.close();
|
||||
}
|
||||
|
||||
clearInterval(send_ref);
|
||||
send_ref = null;
|
||||
if (send_ref) {
|
||||
clearInterval(send_ref);
|
||||
send_ref = null;
|
||||
}
|
||||
showStats(); // Final numbers
|
||||
recv_seq = 0;
|
||||
send_seq = 0;
|
||||
|
@ -264,26 +273,12 @@
|
|||
}
|
||||
|
||||
|
||||
/* If no builtin websockets then load web_socket.js */
|
||||
if (window.WebSocket) {
|
||||
VNC_native_ws = true;
|
||||
} else {
|
||||
VNC_native_ws = false;
|
||||
message("Loading web-socket-js flash bridge");
|
||||
var extra = "<script src='include/web-socket-js/swfobject.js'><\/script>";
|
||||
extra += "<script src='include/web-socket-js/FABridge.js'><\/script>";
|
||||
extra += "<script src='include/web-socket-js/web_socket.js'><\/script>";
|
||||
document.write(extra);
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
console.log("onload");
|
||||
if (VNC_native_ws) {
|
||||
if (Websock_native) {
|
||||
message("Using native WebSockets");
|
||||
} else {
|
||||
message("initializing web-socket-js flash bridge");
|
||||
WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
|
||||
WebSocket.__initialize();
|
||||
}
|
||||
var url = document.location.href;
|
||||
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>IRC Client using WebSockets</title>
|
||||
<script src="include/base64.js"></script>
|
||||
<script src="include/websock.js"></script>
|
||||
<script src="include/util.js"></script>
|
||||
<script src="include/webutil.js"></script>
|
||||
<script src="include/keysym.js"></script>
|
||||
<script src="include/VT100.js"></script>
|
||||
<script src="include/wsirc.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'>
|
||||
<input id='connectButton' type='button' value='Connect' style='width:100px'>
|
||||
<br>
|
||||
Nick: <input id='nick' style='width:120'>
|
||||
<br>
|
||||
Channel: #<input id='channel' style='width:70'>
|
||||
|
||||
<br><br>
|
||||
|
||||
<div><pre id="irc"></pre></div>
|
||||
>
|
||||
<input id="msg" type="text" size=80 onkeypress="sendMsg();">
|
||||
|
||||
<script>
|
||||
var irc;
|
||||
|
||||
function sendMsg() {
|
||||
if (event.keyCode === 13) {
|
||||
var msg = $D('msg').value;
|
||||
$D('msg').value = "";
|
||||
|
||||
Util.Debug("calling sendMsg('" + msg + "')");
|
||||
irc.sendMsg(msg);
|
||||
}
|
||||
}
|
||||
|
||||
function connect() {
|
||||
var ret;
|
||||
ret = irc.connect($D('host').value,
|
||||
$D('port').value,
|
||||
$D('encrypt').checked,
|
||||
$D('nick').value,
|
||||
$D('channel').value);
|
||||
if (! ret) { return false; }
|
||||
$D('connectButton').disabled = true;
|
||||
$D('connectButton').value = "Connecting";
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
$D('connectButton').disabled = true;
|
||||
$D('connectButton').value = "Disconnecting";
|
||||
irc.disconnect();
|
||||
}
|
||||
|
||||
function connected() {
|
||||
$D('msg').disabled = false;
|
||||
$D('connectButton').disabled = false;
|
||||
$D('connectButton').value = "Disconnect";
|
||||
$D('connectButton').onclick = disconnect;
|
||||
}
|
||||
|
||||
function disconnected() {
|
||||
$D('msg').disabled = true;
|
||||
$D('connectButton').disabled = false;
|
||||
$D('connectButton').value = "Connect";
|
||||
$D('connectButton').onclick = connect;
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
console.log("onload");
|
||||
var url = document.location.href;
|
||||
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
|
||||
$D('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
|
||||
$D('nick').value = (url.match(/nick=([^&#]*)/) || ['',''])[1];
|
||||
$D('channel').value = (url.match(/channel=([^&#]*)/) || ['',''])[1];
|
||||
|
||||
disconnected();
|
||||
|
||||
irc = IRC('irc', connected, disconnected);
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
Reference in New Issue