If you are developing an application that requires low latency (human-unnoticeable delays between an input being processed and the corresponding output) like a chat room application, traditional HTTP requests are going to be problematic. The traditional request/response paradigm that the web was built around does not lend itself well to low latency. And furthermore, all communication using this paradigm is initiated by the client. But what if we need the server to initiate communication, for instance when we have two chat room clients and one submits a message. In order for the second user to see that message, we could set up AJAX requests from the client to check periodically for new messages (this is called Long-Polling and there's a good explanation of this in this stackoverflow post). But we could also have the server initiate communication wih the second client to let them know a message was submitted. This is exactly what WebSocket does.

WebSockets, what are they?

In plain English, a WebSocket is a "socket" connection between a web browser and a server that is a persistent connection. Either party can start sending data at any time. The data transferred can be text or binary. So, it can be a simple JSON payload or binary message formats (blob or array buffer).

In order to initiate a new WebSocket connection, all we need is the URL of a WebSocket resource and some callback methods. If you are using plain old JavaScript, here's what opening a WebSocket connection would look like.

var connection = new WebSocket('ws://examplesite.org/echo', ['soap', 'xmpp']);

Notice the ws before the url. This establishes the prototocol to be used for communication, much like you would see http in a traditional url. Note: there is also wss for secure WebSocket connections.

The methods that we would make use of if we were using native JavaScript would be:

1) ws.onerror = function(error) { ... } 
2) ws.onclose = function() { ... }
3) ws.onopen = function() {
    ws.send("Connection established. Hello World!");
    }
4) ws.onmessage = function(msg) {
       if(msg.data instanceof Blob) {
           processBlob(msg.data);
       } else {
           processText(msg.data);
       }
   }    
  1. ws.onerror will be invoked if connection error occurs.
  2. ws.onclose invoked when the connection closes
  3. ws.onopen invoked when a WebSocket connection is established. ws.send is a client-initaed message to the server.
  4. ws.onmessage is callback that is invoked with each new message received from the server. You can check if the msg.data received is binary or text and then act accordingly.

So, we now have the idea of what using WebSocket would look like using native JS, but there are problems with implementations using native JS, namely that not all browsers have implemented the standard the same and for this reason, much like why jQuery is so popular, we need a cross-browser solution. This is where the library socket.io comes in. Developed by Automattic, the same company that developed Wordpress, socket.io is fairly straightforward to implement, even if the documentation is a little lacking.

Let's take the example of a simple chat room and look at the server-side code in Node.js and the client-side code. One of the elegant things about socket.io is it's seamless ability to fall-back to long-polling (check out the stackoverflow link above if you aren't sure about long-polling) if a WebSocket connection isn't available. The way we do this is we first create an http server in Node.js and then use it in socket.io server. Below we show how to do this using Express.js framework on top of Node.js.

var app = require('express')();
//create http server first
var server = require('http').Server(app);
//build socket.io server on top of http server
var io = require('socket.io')(server);

server.listen(80);

//route for GET request to root
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});

//when browser connects to server
io.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
//when server receives the event called new event
socket.on('new event', function (data) {
   console.log(data);
  });
});

Now let's discuss how to handle room-level interaction using socket.io. This is useful in a chat application if you want users to be able to join a room and see messages posted for that room only.

Socket.io includes the following methods:

//join a room
socket.join('room1');
//leave a room
socket.leave('room1');

//broadcast messages to a certain room (excluding the client)
socket.broadcast.to('room1').emit('function', 'data1', 'data2');

//broadcast messages to everyone in the room (including the client)
io.sockets.in('room1').emit('function', 'data1', 'data2');

An example of a client actually joining a room could look like this. In this example, we are not using a database to store username but in a real application, we would implement a database and store user information there.

The following would be added below the basic setup shown earlier that uses Express.js and socket.io.

// usernames of users currently connected to  chat
var usernames = {};

// rooms currently available in chat
var rooms = ['Library','Study','Dining Room'];

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

  // when the client emits 'joinroom'
  socket.on('joinroom', function(username){
      // store this client's username in the socket session
      socket.username = username;
      // store the room name in the socket session for this client
      socket.room = 'Library';
      // add the client's username to the global list
      usernames[username] = username;
      // send client to room 1
      socket.join('Library');
      // echo to client they've connected
      socket.emit('updatechat', 'SERVER', 'you have connected to Library');
      // echo to Library that a person has connected to their room
      socket.broadcast.to('room1').emit('updatechat', 'SERVER', username + ' has connected to this room');
      socket.emit('updaterooms', rooms, 'room1');
  });

  // when the client emits 'sendchat', this listens and executes
  socket.on('sendchat', function (data) {
      // we tell the client to execute 'updatechat' with 2 parameters
      io.sockets.in(socket.room).emit('updatechat', socket.username, data);
  });

  socket.on('switchRoom', function(newroom){
      // leave the current room (stored in session)
      socket.leave(socket.room);
      // join new room, received as function parameter
      socket.join(newroom);
      socket.emit('updatechat', 'SERVER', 'you have connected to '+ newroom);
      // sent message to OLD room
      socket.broadcast.to(socket.room).emit('updatechat', 'SERVER', socket.username+' has left this room');
      // update socket session room title
      socket.room = newroom;
      socket.broadcast.to(newroom).emit('updatechat', 'SERVER', socket.username+' has joined this room');
      socket.emit('updaterooms', rooms, newroom);
  });

  // when the user disconnects.. perform this
  socket.on('disconnect', function(){
      // remove the username from global usernames list
      delete usernames[socket.username];
      // update list of users in chat, client-side
      io.sockets.emit('updateusers', usernames);
      // echo globally that this client has left
      socket.broadcast.emit('updatechat', 'SERVER', socket.username + ' has disconnected');
      socket.leave(socket.room);
  });
});

And on the client side, the code would look like this:

var socket = io.connect('http://localhost:8080');

// on server connection, ask for user's name with callback
socket.on('connect', function(){
	// call the server-side function 'adduser' 
	socket.emit('adduser', prompt("Enter Your Name"));
});

// listener, whenever the server emits 'updatechat', this updates the chat body
socket.on('updatechat', function (username, data) {
	$('#conversation').append('<b>'+username + ':</b> ' + data + '<br>');
});

This has demonstrated a simple use for socket.io. Please leave comments or questions below.