Cacomania: WebSockets chat with Node.js

Cacomania

WebSockets chat with Node.js

Guido Krömer - 28. October 2012 - Tags: ,

Hi folks, this is the second part of my journey through the world of Node.js. If you want to read the first part, about a socket based chat server, go here. As seen in the title I made a small chat server, too. Which is now WebSocket based. The WebSocket implementation I used is ws which is installed by npm as follows:

$ npm install ws

Since WebSockets are not supported by the Internet Explorer 9 and below the example will run on "only" on Firefox, Chorme, Webkit and Opera.
enable WebSockets in opera 12
In Opera you will have to go to the "about:config" page for enabling this feature.

The server

But now lets get a little bit more concrete, if you have read the first part you might have seen that all server code is in one file which was fine for the small tutorial but this is not necessary. With nodejs require functionality you can load additional files, like the client.js file below. It gets loaded by the clients.js, which is maybe not the best and significant naming, using var Client = require('./client.js').Client.

The Clients class is handling all logic needed on the server side, and quite similar to the Clients class used for the traditionally socket server. Since the client is now browser based and not text only the server can send commands to the client, too. If a new user connects a list with all user names actually connected to server gets send to all clients for example. The disconnection is a little bit different to a normal socket connection, if a browser windows get closed the connection can be still open for a while and the "close" callback might not be raised before a new message will be send by the server. Therefore the sending part is encapsulate in a separate method which will call removeClient when an exception was thrown. When a client gets disconnected the "/removed" command followed by the user name gets send to the still connected clients.

this.sendToClient = function (client, msg) { 
  try { 
    client.send(msg) 
  } 
  catch (error) { 
    console.log(error) 
    if (error == 'Error: not opened') { 
      this.removeClient(null, client.name) 
    } 
  } 
}

This is the Clients class sendToClient method wich call the removeClient Method on the specified WebSocket error.

New in comparison to the socket version is the forEachClient method for iterating over all clients:

this.forEachClient = function (callBack) { 
  for (var i = clients.length - 1; i >= 0; i--) { 
    if (!clients[i]) { 
      clients.splice(i, 1) 
      continue 
    } 
    callBack(clients[i], i) 
  } 
}

It has been used in the sendToAll method for example, var thath = this is a little workaround for accessing the public methods in the callback, too.

this.sendToAll = function (msg) { 
  var thath = this 
  this.forEachClient(function (client, i) {
    thath.sendToClient(client, msg) 
  }) 
}

The websocket-server.js handles the incoming WebSocket connections and messages. Because the client is browser based its a small http server, too. The web server functionality is limited to serving the needed html, css and js files and should not be used in a production environment. Therefore it is limited listening on your localhost. Serving static files are a task which should be performed by a classic webserver like an apache or lighty. But for tutorial purposes the webserver is quite interesting, it uses a callback (fs.readFile('Client/' + req.url, function (err, data) {...) for serving the requested files, limited to the three needed mime types. The usage of a callback for file reading ensures that the server is still non blocking, even if the file systems gets slow.

The client

The client code is very rudimentary checks like if the connection has gone or if the browser does not support WebSockets are missing for simplicity. Because of the limited functionality I made my decision against jQuery for the front-end. There is just a kind of poor mans jQuery at the beginning of the code, function $ (elementId) { return document.getElementById(elementId) }, for finding the necessary dom elements by the given id.

webSocket.onmessage = function (msg) { 
  if (command(msg.data)) { 
    return 
  } 
  addMessage(msg.data, 'public') 
}

WebSocket chat appliction running in chrome and firefox.

This is the WebSocet chat running on two browsers, Mozilla Firefox and Chromium, not really nice but is works :) .

This is the whole client code, addMessage appends a new message to the chat area and removes old messages. On each addMessage call the chat area scrolls down chatArea.scrollTop = chatArea.scrollHeight and removes the old messages automagically chatArea.removeChild(oldMessage). addUser adds new users to the users area on the right, and appends an onclick event for sending private messages or changing the user name. If a command was send to the client the command function evaluates the given command. The /name_change command will call changeUserName(params[0], params[1]) to change the username in the users area on the right.

All code can be found in gist, have fun playing around with the code. I'm going to write a third part of this Node.js chat tutorial with a ajax chat which should work on the Internet Explorer, too.