Part one | Part three

In this series of posts we are going to show you how to use node.js and Socket.IO to build a simple chat application, and then mix it up with Google Maps and the Geolocation API to create a cool geographic app. We will use Bootstrap with a modified css to get a nice look for the website.

In the last post we showed you how to create a simple web server with Node.js. In this post we will show you how to use Socket.IO to send chat messages.

Socket.IO is a framework for Node.js that allows the creation of real-time web based applications which, as we will show you next, lets you build something pretty neat in no time.

As Node.js, Socket.IO is also event based. One great feature is that it allows you to easily define custom events. A socket owner can emit any events he wants, and listen to events that he is interested in.

Let's begin by defining the events we will use to communicate between the chat client and the server. First the events that the client will emit:

  • join: To join the chat room.
  • message: When sending a chat message.

And now the events that the server will send to the clients:

  • user connected: When a new user joins the chat room.
  • user disconnected: When a user leaves the chat room.
  • new chat msg: When a new chat message arrives.

The client will suscribe to these last three events in order to receive chat messages and update the number of users in the chat room.

Now that we have defined the events we can begin coding our Socket.IO-powered server. We will store the users information in a variable, so that we can broadcast the chat messages accordingly.

var connectedUsers = {};
var io = require('socket.io');
var webSocket = io.listen(httpServer);

Here we are using Socket.IO with the Node HTTP server we created, but it can also be set to run standalone.

webSocket.sockets.on('connection', function(socket) {

Inside this function we will add our event listeners. Setting a listener is really simple, just tell Socket.IO the name of the event and the callback function to handle it.

  socket.on('join', function(user, sendKey) {
  user.key = Date.now();
  connectedUsers[user.key] = user;
  sendKey(user.key);
  socket.set('userkey', user.key);
Here we take the data from the event, a user in this example, and stash it in a variable. The event callback can receive an arbitrary number of parameters.
There is a special case when the last parameter is a function, it will act as an acknowledgement. This acknowledgement can also contain data, in that way it resembles a remote function call.

The socket object allows you to save data associated with the client session by using set, in this example we are saving a generated user id and passing it back to the client. This could be a nice way to set an authorization token.

Finally, we want to notify users that are already in the chat room that another users has joined in.
socket.broadcast.emit("user connected", user);
});

The broadcast flag we use here tells Socket.IO to send the message to all the sockets that are connected to the server, except the one that triggered the event.

The other event is similar, we use get to retrieve the data we saved before.

socket.on('message', function(msg) {
  socket.get('userkey', function(err, key) {
    var user = connectedUsers[key];
    if(user) {
      var data = {
        key : key,
        sender : user.name,
        message : msg
      };
      socket.broadcast.emit('new chat msg',data);
    }
  });
});

Socket.IO provides several default events, you might want to check the docs. Here we handle the disconnection of a client.

socket.on('disconnect', function() {
  socket.get('userkey', function(err, key) {
    var userInfo = connectedUsers[key];
    if(userInfo) {
      delete connectedUsers[key];
      socket.broadcast.emit("user disconnected", key);
    }
  });
});

Now we need to work out the client side. In order to use Socket.IO from the browser you must add a reference to the client script. If you are using node.js as your web server then it will serve the file for you, just add the following reference to the HTML:

  <script src="/socket.io/socket.io.js"></script>

Here we present a simplified version of the chat client, so that you don't get overwhelmed with presentation details and we can focus in the Socket.IO bits.

var user = {};
var usersCount, messagesList, messageBox, sendButton, webSocket;

function initialize(username) {
 user.name = username;

 webSocket = io.connect(window.location.hostname);
 webSocket.on("user connected", addUser);
 webSocket.on("user disconnected", removeUser);
 webSocket.on('new chat msg', receiveChatMessage);

 sendButton.on('click', sendChatMessage);

 var userName = { name : user.name };

 webSocket.emit('join', userName,
 // Callback function
 function(key){
 user.key = key;
 }
 );
}

function addUser(user){
 ++usersCount;
}

function removeUser(key){
 --usersCount;
}

function receiveChatMessage(data) {
 // Add the received message to the list
 messagesList.append(data.message);
}

function sendChatMessage() {
 // Obtain messsage from textarea
 var messageVal = messageBox.val();
 messagesList.append(messageVal);
 // Empty textarea
 messageBox.val('');
 // Send the message
 webSocket.send(messageVal);
}
First we register listeners to each event, passing our event handlers. After that we emit a join message, which will allow us to enter the chat room and send messages. Notice that we use a callback function to receive a session key after connection, these acknowledgements come in real handy to return success or error messages.

Here we use a shorthand version to emit the "messages" event, socket.send. This allows you to code against the WebSockets API leveraging Socket.IO cross-browser support.

We hope that you have understood the basics on Socket.IO events, you can check and download the full source code from github. In the next post we will use Socket.IO combined with Google Maps to send geographic updates in real time, and we will show you what you need to do to upload your Node app to heroku.

Curious? You might want to give a try to our live demo.

Check out other posts in this series:


Next - Part three
Previous - Part one

Comments: