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:
Joel Martin 2011-01-23 19:30:51 -06:00
parent 307dda1ad0
commit 3d91fd3492
6 changed files with 390 additions and 52 deletions

View File

@ -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

View File

@ -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

View File

@ -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;

235
include/wsirc.js Normal file
View File

@ -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()

View File

@ -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];

99
wsirc.html Normal file
View File

@ -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'>&nbsp;
Port: <input id='port' style='width:50'>&nbsp;
Encrypt: <input id='encrypt' type='checkbox'>&nbsp;
<input id='connectButton' type='button' value='Connect' style='width:100px'>
<br>
Nick: <input id='nick' style='width:120'>&nbsp;
<br>
Channel: #<input id='channel' style='width:70'>&nbsp;
<br><br>
<div><pre id="irc"></pre></div>
&gt;
<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>