Part 1: Real-Time Browser to Browser Communication Using Node.Js and Socket.Io

Usually browsers communicate with the server, sending a request and getting a response. But what if it would be possible to link two or more browsers, while using the server to route requests between the two? With the advance of server side JavaScript, using node.js, and socket connections supported by HTML5 capable browsers (or frameworks to emulate the functionality in older versions – i.e. Socket.Io) this task has become trivial. 

Node.js provides a server side JavaScript implementation, allowing for event driven, non-blocking I/O programming, enabling real-time application development. It relies on Google Chrome's JavaScript runtime, currently (node.js version 0.11.1) implementing the ECMA-262 ECMAScript Language Specification.

Socket.Io is a JavaScript framework, that enables real time browser to server communication, providing an API that encapsulates different transport mechanisms. It's browser compatibility starts with versions of IE5.5, Safari 3, Firefox 3, Opera 10.61, Google Chrome 4 as well as mobile browsers such as iPhone/iPad Safari and Android/WebOS Webkit. The underlying transport mechanisms are based on Websocket, Adobe Flash Socket, AJAX long polling, AJAX multipart streaming, Forever Iframe and JSONP Polling. The transport mechanism is automatically selected without the developer having to specify it.

This article assumes both tools are available on your server. Node.js is available as a port in FreeBSD, named 'node' and as a package in Debian, named 'nodejs'. Socket.io can be installed using Node.js' NPM package manager ('npm' port/package in FreeBSD and Debian), as described here.

Below I am presenting the source code of a basic connection application, that adds a console message when the client connects or disconnects.

Server side: Accepting new connections, and handling connect and disconnect events

Let's begin writing an object, that handles the socket's connect and disconnect events. For good design, this object follows the JavaScript Revealing Prototype Pattern.

Step 1: Prepare a 'Server' class, that takes in a configuration object through it's constructor.

/**
 * Messaging server.
 * @class Provides server functionality.
 * @constructor
 * @param {Object} config Server configuration object.
 * Supported keys are: port (listening port number),
 * and socket (socket listener configuration object).
 */
var Server = function( config ) {
	// Store configuation, in a 'private' property
	this._config = config;
}

Step 2: Add a function that loads the required libraries, and prepares required listeners.

/**
 * Method used for loading the 'http' and 'socket.io' classes,
 * and creating the listeners.
 * @function
 */
Server.prototype.loadLibraries = function() {
	// HTTP Server, required for serving Socket.Io JS file
	// NOTE: The HTML file that makes use of it,
        // is served by a standard HTTP server.
	this._httpServer = require('http').createServer();

	// Socket.Io
	this._socketIo = require('socket.io').listen(
		this._httpServer
		,this._config.socket
	);
}

Step 3: Add a function to open the configured listening port, and attach the two event handlers.

/**
 * Method used for preparing the server listeners, and attaching event handlers.
 * @function
 */
Server.prototype.init = function() {
	// Load required libraries.
	this.loadLibraries();

	// Open port for incoming connections
	this._httpServer.listen( this._config.port );

	// Attach a Socket.Io connection handler
	// This handler in turn will attach application specific event handlers.
	this._socketIo.sockets.on( 'connection', function ( socket ) {
		// Attach the socket disconnect handler
		// This handler in turn will attach application specific event handlers.
		socket.on( 'disconnect', function () {
			// TODO: Add disconnect event handlers here
			console.log( 'Client disconnected: ' + socket.handshake.address.address );
		}.bind( this ) );

		// TODO: Add connection event handlers here
		console.log( 'Connection from: ' + socket.handshake.address.address );
	}.bind( this ) );
}

Step 4: Create a new Server instance.

// Create server
new Server( {
	port: 10000 // Listening port
	,socket: { // Socket configuration
		log: false // Disable logging
	}
} ).init(); // Call the init function

Once the code is ready, you can start the node.js server:

nodejs server.js

Client side: Initiate a connection

Node.Js can itself be used to serve the HTML/JS content. In this example, the node.js server provides the socket.io JavaScript code, while the client JavaScript and HTML code is served by any server. Similar to the server code, this object makes use of the Revealing Prototype Pattern.

Step 1:  Prepare a 'Client' class, that takes in a configuration object through it's constructor.

/**
 * Messaging client.
 * @class Provides client functionality.
 * @constructor
 * @param {Object} config Client configuration object. 
 * Supported keys are: port (listening port number), host (URL)
 */
var Client = function( config ) {
	// Store configuation, in a 'private' property
	this._config = config;
}

Step 2: Add a function that opens a connection, and attaches a basic 'connect' event handler.

/**
 * Method used for initiating a connection, and attaching event listeners.
 * @function
 */
Client.prototype.init = function() {
	// Create connection
	this._socket = io.connect( this._config.host + ':' + this._config.port );

	// Attach a 'connect' event handler.
	this._socket.on( 'connect', function() {
		// TODO: Add 'connection' event handler.
		console.log( "Connected." );
	}.bind( this ) );

	// TODO: Add other events here.
}

Step 3: Create a client instance.

new Client( {
	port: 10000
	,host: 'http://localhost'
} ).init();

Step 4: Load the required files in an HTML file. The socket.io.js file is served by the node.js server, while the client.js file is served by another HTTP server. 

<script src="http://localhost:10000/socket.io/socket.io.js"></script>
<script src="client.js"></script>

In a next article, I will present how to extend this object and add application specific events and handlers, to allow message routing between multiple clients.

Leave a Reply

Your email address will not be published. Required fields are marked *