Source: network/WebSocketMetrics.js

/**
 * WebSocketMetrics class.
 *
 * @param {!Grape2D.WebSocket} webSocket WebSocket to bind.
 * @param {!Grape2D.utils.SynchronizedClock=} syncClock A clock
 *   synchronized with the server.
 * @constructor
 */
Grape2D.WebSocketMetrics = function(webSocket, syncClock) {
	/**
	 * Web Socket binded.
	 *
	 * @type {!Grape2D.WebSocket}
	 * @public
	 */
	this.ws = webSocket;
	/**
	 * Accumulated ping.
	 *
	 * @type {!number}
	 * @private
	 */
	this.pingAc = 0;
	/**
	 * Ping responses received.
	 *
	 * @type {!number}
	 * @private
	 */
	this.pingSamplesReceived = 0;
	/**
	 * Accumulative clock synchronization.
	 *
	 * @type {!number}
	 * @private
	 */
	this.syncAc = 0;
	/**
	 * Ping samples to send to the server.
	 *
	 * @type {!number}
	 * @private
	 */
	this.pingSamples = 10;
	/**
	 * Last ping value registered.
	 *
	 * @type {!number}
	 * @private
	 */
	this.pingValue = 0;
	/**
	 * Last time the ping value was registered.
	 *
	 * @type {!number}
	 * @private
	 */
	this.lastPing = 0;
	/**
	 * Total number of bytes sent.
	 *
	 * @type {!number}
	 * @private
	 */
	this.bytesSent = 0;
	/**
	 * Total number of bytes received.
	 *
	 * @type {!number}
	 * @private
	 */
	this.bytesReceived = 0;
	/**
	 * Time when the web socket was opened.
	 *
	 * @type {!number}
	 * @private
	 */
	this.start = 0;
	/**
	 * Bytes of the last message received.
	 *
	 * @type {!number}
	 * @private
	 */
	this.lastReceived = 0;
	/**
	 * Bytes of the last message sent.
	 *
	 * @type {!number}
	 * @private
	 */
	this.lastSent = 0;
	/**
	 * Synchronized clock with the server.
	 *
	 * @type {!Grape2D.utils.SynchronizedClock}
	 * @private
	 */
	this.syncClock = syncClock || new Grape2D.utils.SynchronizedClock();

	var that = this;
	this.ws.addOnOpen(function() {
		that.start = that.syncClock.getTime();
	});
	this.ws.addOnMessage(function(message) {
		var byteSize = message.length;
		that.bytesReceived += byteSize;
		that.lastReceived = byteSize;
		if (message.indexOf(Grape2D.WebSocketMetrics.PING_PREFIX) === 0) {
			that.pongReceived(message);
		}
	});
	this.ws.addOnSend(function(message) {
		var byteSize = message.length;
		that.bytesSent += byteSize;
		that.lastSent = byteSize;
	});
};

Grape2D.WebSocketMetrics.prototype = {
	constructor: Grape2D.WebSocketMetrics,
	/**
	 * Sends a ping message.
	 *
	 * @public
	 */
	ping: function() {
		this.pingStop = true;
		this.pingAc = 0;
		this.syncAc = 0;
		this.pongSamplesReceived = 0;
		var that = this;
		for (var i = this.pingSamples; i > 0; i--) {
			setTimeout(function() {
				that.ws.send(Grape2D.WebSocketMetrics.PING_PREFIX + " " + (new Date().getTime()));
			}, 20 * i);
		}
	},
	/**
	 * Sends back a pong message.
	 *
	 * @param  {!string} message Ping message, received.
	 * @public
	 */
	pong: function(message) {
		this.ws.send(message + " " + this.syncClock.getTime());
	},
	/**
	 * Pong message handler. This is used to calculate the ping
	 *   and synchronize the clock with the server.
	 *
	 * @param  {!string} message Pong response message.
	 * @public
	 */
	pongReceived: function(message) {
		var t4 = new Date().getTime(),
			d = message.split(" "),
			t1 = Number(d[1]),
			t2 = Number(d[2]),
			t3 = t2,
			theta, delta;
		theta = ((t2 - t1) + (t3 - t4)) * 0.5;
		delta = (t4 - t1);
		this.pingAc += delta;
		this.syncAc += theta - delta * 0.5;
		this.pingSamplesReceived++;
		this.lastPing = t4;
		if (this.pingSamplesReceived == this.pingSamples) {
			this.pingValue = Grape2D.Math.ceil(this.pingAc / this.pingSamples);
			this.pingSamplesReceived = 0;
			this.syncClock.sync(Grape2D.Math.floor(this.syncAc / this.pingSamples));
		}
	},
	/**
	 * Gets the network latency, in milliseconds.
	 *
	 * @return {!number} Ping value.
	 * @public
	 */
	getPing: function() {
		return this.pingValue;
	},
	/**
	 * Gets the last time a pong response was received.
	 *
	 * @return {!number} Timestamp, in milliseconds.
	 * @public
	 */
	getLastPing: function() {
		return this.lastPing;
	},
	/**
	 * Gets the number of bytes sent, on the last message.
	 *
	 * @return {!number} Bytes sent.
	 * @public
	 */
	getLastBytesSent: function() {
		return this.lastSent;
	},
	/**
	 * Gets the total number of bytes sent.
	 *
	 * @return {!number} Bytes sent.
	 * @public
	 */
	getBytesSent: function() {
		return this.bytesSent;
	},
	/**
	 * Gets the number of bytes sent per second. Average
	 *   between the total number of bytes sent and the
	 *   time since the web socket was open.
	 *
	 * @return {!number} Average bytes per second.
	 * @public
	 */
	getBytesSentPerSec: function() {
		return this.bytesSent / ((new Date().getTime()) - this.start);
	},
	/**
	 * Gets the number of bytes received, on the last message.
	 *
	 * @return {!number} Bytes received.
	 * @public
	 */
	getLastBytesReceived: function() {
		return this.lastReceived;
	},
	/**
	 * Gets the total number of bytes received.
	 *
	 * @return {!number} Bytes received.
	 * @public
	 */
	getBytesReceived: function() {
		return this.bytesSent;
	},
	/**
	 * Gets the number of bytes sent per second. Average
	 *   between the total number of bytes sent and the
	 *   time since the web socket was open.
	 *
	 * @return {!number} Average bytes per second.
	 * @public
	 */
	getBytesReceivedPerSec: function() {
		return this.bytesReceived / ((new Date().getTime()) - this.start);
	}
};
/**
 * Prefix of the ping message. Any message with this prefix
 *   is treated as a ping message.
 *
 * @type {!string}
 * @public
 * @constant
 */
Grape2D.WebSocketMetrics.PING_PREFIX = "'";