
Amazing World of Chat – Illustration by kdonovan_gaddy
Websockets is amazing technology in the new HTML5 specifications that allows for a new way of communicating between browsers and servers. Through this technology, applications can easily and quickly provide bi-directional communication in realtime. This allows developers to create all sorts of realtime applications (e.g. statistics, chat applications) without the need of relying on huge AJAX calls that continuously poll the server and take up large amounts of resources. With Socket.io, working with Websockets is astoundingly easy and works perfectly with the non-blocking and quick realtime server, NodeJS. Plus, with Socket.io, browser compatibility for Websockets is no longer an issue as it solves the problem by choosing whatever communication protocol available for the browser at hand. So, let’s have a peek at parts of an application that uses Socket.io, a program I am developing called LightChat.
The Aim
A chat application is the simplest way of demonstrating the deliciousness of Websockets and Socket.io. For this, we will just need the user to be able to:
- Send a chat message to be broadcasted to everyone that is ‘connected’
- Receive a chat message
This tutorial requires the following software / node modules. Be sure to download them through the links below or through npm.
- NodeJS (v 0.6.9)
- CoffeeScript (v 1.2.0)
- Express.js (v 0.5.8)
- Jade Template Engine (v 0.20.0)
- Socket.io (v 0.8.7)
1. Setting Up
Again, we can set up our project structure as in my previous two tutorials. If you need help with creating this structure again, please have a look at one of my previous posts. Next, we should create our application file in our root folder. I’m calling mine main.coffee:
exp = require 'express'
app = exp.createServer()
io = require('socket.io').listen app
app.configure ->
app.set 'views', __dirname + '/views'
app.set 'view engine', 'jade'
app.use exp.static __dirname + '/public'
app.listen 1337
console.log 'Server running at http://localhost:1337'
If you have seen any NodeJS application before, this will be one of the most simple configurations but in this example, it is simply all we need. The only new thing is perhaps line 3 which requires Socket.io and then configures it to listen to the application created by Express. Apart from that, we are doing everything very standard here.
2. Fun with Socket.IO
Here comes the fun part. When Socket.io makes a connection as in the following script, it figures out the browser and chooses the most appropriate communication method, whether that be Websockets, Flash or whatever. So, we need code that triggers connection and that code that fits in main.coffee:
io.sockets.on 'connection', (socket) ->
socket.on 'send', (data) ->
socket.broadcast.emit 'distribute', data
Lines 2 and 3 are the awesomesauce and everything needed to run the chat application. Socket.io works on listeners and senders. However, in the language, these verbs become ‘on’ and ‘emit’, respectively. Line 2 serves as the listener function, waiting for the function ‘send’. When ‘send’ is emitted from the client side (see below), line 3 sends a function named ‘distribute’ which sends the data back. Using the broadcast tag, Socket sends the same data to everyone that is connected except for the user itself.
For the client side, I am going to write a jQuery file called client.coffee under the directory /public/js/:
$ ->
socket = io.connect 'http://localhost'
socket.on 'distribute', (data) ->
$('#results').append('<div>' + data.message + '</div>')
$('#chatter').on 'submit', (e) ->
e.preventDefault & e.preventDefault()
message = $('#message').val()
socket.emit 'send', {'message': message}
$('#results').append('<div>' + message + '</div>')
The client side is slightly more involved but no less simple.
In line 1, the variable socket must be connected to a file that is served automatically on the host. If you have installed socket.io correctly (remember this means globally), it should be served when you require it. In my case, it is on http://localhost.
Using the socket variable, we are able to create listener and sender functions through either socket.on or socket.emit. This works exactly the same as the ones on the server. Now, we can define functions such as ‘send’ on line 9, which passes a variable ‘message’ to the server when a form is submitted. As we saw before, the server will be listening for the ‘send’ function and will broadcast the function ‘distribute’ in return. We can define what happens on functions such as ‘distribute’ as in line 3, in which we append the ‘message’ variable to a element called ‘#results’. Simple.
If that has you muddled up, think of the entire process of sending and receiving one chat message. The user types something in a form, submits it and client.coffee emits a function called send, which sends what the user typed as the variable ‘message’ to the server. The server, on main.coffee, listens for the ‘send’ function and when it receives the function, broadcasts a function called ‘distribute’ and sends the data along to everyone except the user. Everyone that is connected has client.coffee which listens for the ‘distribute’ function. On receipt of ‘distribute’, it appends the message to the screen in the ‘#results’ element.
3. Front-End
Now that we’ve created all the logic and understood it (Yes, that was all there was to it), let us create a simple interface for our chat application.
For this, let’s quickly render an index file in main.coffee when one goes to ‘/’:
app.get '/', (req, res) ->
res.render 'index', {locals: {title: 'LightChat'}}
Now, we can create a layout.jade and an index.jade. The layout.jade will be very simple with the bare basics:
!!! 5
html
head
title= title
body
!= body
However, index.jade will be where all the magic happens:
#results
form#chatter
input#message(type= "text", placeholder= "Type Something...")
input(type= "submit", value= "Send")
script(src= "/js/jquery.min.js")
script(src= "/js/client.js")
As you can see, it is merely an element called ‘#results’, which we will use to append messages and a form called ‘#chatter’, which the user will type in the ‘message’ input to send to the server. Finally, in the last two lines, jQuery and the client file will be served.
4. More Complicated Please
If you’re complaining that this tutorial was too simple (I was!), here is a little bit more, before you go ahead and run everything. You can follow this if you want or jump right down to the next section if you have had enough. Either way, it should run perfectly well (we hope).
I want to show you how easy it is to create a multiroom chat application wherein users who have entered a chatroom only get the messages from people in the chatroom they are in. To simplify the chatroom experience, however, I am going to use URLs as the identifying factor. This means that separate chatrooms are going to separated by the pathname, everyone who is connected to the same pathname e.g. /abc is going to be in the same chatroom.
So, let’s modify the socket.io code in main.coffee and create a new app.get so that we serve index.jade when going to http://localhost:1337/ and then whatever id.
app.get '/:id', (req, res) ->
res.render 'index', {locals: {title: req.params.id}}
io.sockets.on 'connection', (socket) ->
socket.on 'switch', (room) ->
socket.join room
socket.on 'send', (data) ->
socket.broadcast.to(data.room).emit 'distribute', data
You will notice there are some slight differences with this code and the last. Firstly, we grab the ID of the chatroom by calling req.params.id. This only serves to render the index.jade layout and set the title of the page to the ID of the chatroom.
Now, there are two different lines in the socket code. Lines 5 and 6 is where the magic happens. These lines allows us to join a room, when the function ‘switch’ is triggered, a function we are going to set when a person first comes onto a webpage. Then, the last line, Line 9 lets us broadcast the same way as before but only to the room that we are going to specify by adding it in the data.
Now that we’ve changed the server-side, let’s give the server what it needs. The first is to emit a function called ‘switch’ that happens when the user goes to a URL. This function, situated in client.coffee, will pass on the pathname:
room = window.location.pathname.slice 1 socket.emit 'switch', room
Since the pathname contains a slash, I am slicing it to avoid confusion. However, I am pretty sure everything would still run even without it. I am only doing this to keep consistent with the Node side.
Finally, we also have to pass on the room variable when something is sent. To do this, we only have to add the ‘room’ variable when sending in the previous client.coffee:
socket.emit 'send', {'message': message, 'room': room}
Done! Now, you have a chat application capable of multiple chatrooms all separated by simply the URL pathname. Everyone with the same pathname will be able to listen in but users in different pathnames will be in separated chatrooms. Very neat!
5. Running
Again, if you have not got the awesome command line coffee –watch –compile happening, then remember to compile the coffee files before running. Also remember that the client.coffee file needs to compile too. After this, you can go back to your project folder and run:
node main.js
To test it out, I recommend opening two browsers, preferably two different kinds side by side and then posting in one and then posting in the other. And voila! It is amazing in so little code is it not?!
Conclusion
Writing realtime applications like the chat application in this tutorial is amazingly simple with Websockets and Socket.io. And, added with the astounding asynchronous and non-blocking nature of NodeJS, it makes these applications super fast and super simple to write. In this example, we managed to create a chat application in which a user can broadcast a message and users can simultaneously talk to each other with two functions socket.on and socket.emit and around 10 lines of code related to it. With the same functions, any realtime communication between browser and server just became much, much simpler and real fun to write. So, dream up big because realtime communication is now all the more real with Socket.io and NodeJS.

