Fedex’s techie blog Yet Another Tech Blog

18Nov/106

Events with jquery, nodejs and socket.io

As I said in my previous post I'm playing around with socket.io to see possible uses for this fantastic library. I've found cases when a simple message sent from the server to the client seems a little short on functionality. For that scenario I propose to make an event driven communication with the server, here is how.

Problem

Imagine a really basic use of socket.io: when a client connects you send a welcome message and every time it sends you a message is just asking the time, you will have something like this in the server:

var http = require('http'),  
    io = require('socket.io'),

server = http.createServer(function(req, res){
	res.writeHead(200, {'Content-Type': 'text/html'});
	res.write('<h1>Sample server created with NodeJS.</h1>');
	res.end();
});
server.listen(8080);
  
// socket.io 
var socket = io.listen(server); 
socket.on('connection', function(client){ 
  client.send('Hello client'); 
  client.on('message', function(){
  	 client.send((new Date()).getTime());
  })
}); 

In the client side you would have something like:

<!DOCTYPE html> 
<html lang="en"> 
<head> 
  <script type="text/javascript" src="http://cdn.socket.io/stable/socket.io.js"></script>
  <script type="text/javascript">
  	var socket = new io.Socket(null, {port: 8080});
	
	socket.connect();
	socket.on('message', function(message){
		document.getElementById('divTime').innerHTML = message;
	});		
	function GetServerTime() {
		socket.send('');
	}  
  </script>
</head> 
<body>
	<div id="divTime"></div>
	<input type="button" value="Get Server Time" onclick="GetServerTime();"> 
</body> 
</html>

This would print "Hello client" as soon as the client gets connected, after that every time you press the button you will get the time on the server.

Now imagine that you want to identify the messages that you get from the server just to make different actions when it's a welcome message or it's the time. We can use the pattern found in the chat example of socket.io. In the server we get:

socket.on('connection', function(client){ 
  client.send({"announcement" : 'Hello client'}); 
  client.on('message', function(){
  	 client.send({"time": (new Date()).getTime()});
  })
});

Client side:

	socket.on('message', function(message){
		if('time' in message) {
			document.getElementById('divTime').innerHTML = 'Time in server:' + message.time;
		}
		else {
			document.getElementById('divTime').innerHTML = message.announcement;			
		}
	});

Now whenever we get the time, it's properly formated. Let's imagine now that we have another functionality in the server side apart from getting the time, we have to parse the message sent from the client in a similar way to the ones sent from the server, and if we continue applying that pattern we will end up with lots of chained IFs or a long switch to act properly according to the recieved message. And what about code reuse? If we want to dispach some routine every time we get certain message we have to do it inside that structure.

Solution

Both jQuery and nodejs have ways to respond to dynamic events. In the former we can use events, while the second can get the same result using the EventEmitter object. Mix that with an standard message protocol and we'll get something much more powerfull. So let's refactor the code using these techniques.

Server code:

var http = require('http'),  
    io = require('socket.io'), 
	events = require('events'),
		util = require('util'),
	sys = require('sys'),
	
server = http.createServer(function(req, res){
	res.writeHead(200, {'Content-Type': 'text/html'});
	res.write('<h1>Sample server created with NodeJS.</h1>');
	res.end();
});

server.listen(8080);
  
var CustomEventHandler = function(client){
	events.EventEmitter.call(this);

	this.client = client;
};

sys.inherits(CustomEventHandler, events.EventEmitter);

CustomEventHandler.prototype._trigger = function(event, data) {
	this.client.send({ "event" : event, "data" : data});
};

CustomEventHandler.prototype.sendAnnouncement = function(text) {
	this._trigger('sendAnnouncement', text);
};

CustomEventHandler.prototype.sendTime = function(time) {
	this._trigger('sendTime', time);
};

// socket.io 
var socket = io.listen(server); 
socket.on('connection', function(client){ 
	var customEventHandler = new CustomEventHandler(client);
	
	customEventHandler.sendAnnouncement('Hello client');
	
	customEventHandler.on('getTime', function() {
		customEventHandler.sendTime((new Date()).getTime());
	});
	
	client.on('message', function(message){
		if(message.event != undefined) {
			customEventHandler.emit(message.event, message.data);
		}
	});
}); 

Client:

<!DOCTYPE html> 
<html lang="en"> 
<head> 
  <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.js"></script>
  <script type="text/javascript" src="http://cdn.socket.io/stable/socket.io.js"></script>
  <script type="text/javascript">
	(function( $ ){
		$.customEventHandler = function() {
	    	var socket = new io.Socket(null, {port: 8080});
			handler = {};
			
			socket.connect();
			socket.on('message', function(obj){
				if('event' in obj) {
					$(handler).trigger(obj.event, obj.data);
				}
			});
			
			handler._trigger = function(event, data) {
				socket.send({"event" : event, "data" : data});
			}
			
			handler.GetTime = function() {
				handler._trigger('getTime', '');
			}
			
			return handler;					
		}
	})( jQuery );
	
	$(document).ready(function(){
		var customEventHandler = $.customEventHandler();
		
		$(customEventHandler).bind('sendTime', function(event, time){
			$('#divTime').text('Time in server:' + time);
		});
		
		$(customEventHandler).bind('sendAnnouncement', function(event, announcement){
			$('#divTime').text(announcement);
		});
		
		$('#btnGetTime').click(function(){
	    	customEventHandler.GetTime();
		});		
	});
</script>
</head> 
<body>
	<div id="divTime"></div>
	<input type="button" id="btnGetTime" value="Get Server Time"> 
</body> 
</html>

As you can see we have considerably more code but it gives us more flexibility as well. Any time we want to add a new event we just bind to it on the server or the client side and dispatch it whenever we need it, this also brings another important feature: the ability to bind an event any time we want and from anywhere. So if we have some places where we have to do some specific stuff when the user get's connected we just $(customEventHandler).bind('sendAnnouncement', customFunction);

Finally

For apps that send simple text messages this technique is a little overwhelming but as explained, as the application grows this will seems a little price to pay to get everything well modularized, and reusable. For example now it's trivial to make the "customEventHandler" a module. In the next article about nodejs and socket.io I'll digging on this topic.

Comments (6) Trackbacks (1)
  1. I could not have possibly gotten my socket.io working with my node.js and my Jquery without the help of this article

    Thank you!

    I’ll be sure to let you know when I get done with my app

  2. Great article! One question:

    In the second code block above (where server code is 6 lines and client is 8 lines), how does the client know the content of the welcome message (‘Hello Client’)?

    It appears as though the server only passes the announcement on connect, not when “socket.on(‘message’…” is called. How does the client know the contents of anything other than message.time. Isn’t message.time all thats available because thats whats in scope of scoket.on. Message.annoucement is defined outside socket.on(‘message’..), so how is it available?

    Thanks,
    Nathan

  3. I need this code working on my pc but not working with proper responce.
    No any time update or any thing is displaying on my page.

    Please Let me know the proper code as I have just copied both code.

    Thanks and Regards,
    Sunny

  4. Hi,
    Following is the code for the Connection with socket
    here I have added one line for the log but i am not reaching there
    It meas , connection is not done.
    ——————————————————————————-
    socket.on(‘connection’, function(client){
    >>> console.log(‘Connection for Socket Is Called’);
    var customEventHandler = new CustomEventHandler(client);

    customEventHandler.sendAnnouncement(‘Hello client’);

    customEventHandler.on(‘getTime’, function() {
    customEventHandler.sendTime((new Date()).getTime());
    });

    client.on(‘message’, function(message){
    if(message.event != undefined) {
    customEventHandler.emit(message.event, message.data);
    }
    });
    });

    ————————————————————————————

    Here Socket.on is not working as per Needed ..
    So at serverSide Following error msg is coming…
    [unhandled socket.io url ]

    Please Give any solution ..

    Thanks in Advance.


Leave a comment