Simple Chat application using node.js and socket.io

In light of my previous posts explaining a websocket based multi-player game, I thought I'd give an introduction to websockets in general and how I got to learn more about them. The best way to understand how websockets work is to create a simple chat application that allows people to chat with each other realtime. And this is exactly what I'm going to explain in this post.

We all remember the document.write("Hello World"); statements that we wrote about 10 (or 15?) years ago, at least this would have been a typical JavaScript statement that I wrote, and of course let's not forget the ever-so-popular var today = new Date(); document.write(today);. JavaScript has progressed from this to DOM manipulation via various frameworks and libraries such as prototype and jQuery. Using these methods, one could create AJAX callbacks, add/disable button, create carousels, light-boxes...the opportunities are endless. And when we thought that's it, JavaScript keeps surviving, evolving and becoming better and better - and as a result of that we have node.js, AngularJS, Backbone.js and a whole lot of new JavaScript frameworks that can help you achieve a lot.

When I first heard about node.js my initial thoughts were "what the fudge is a server-side JavaScript?!" I couldn't envision this setup based on my previous - albeit somewhat limited - knowledge of this language. Once I started to work with node.js, I understood everything. Just to give you a little bit of a background, node.js uses Google's V8 JavaScript engine, which is blazingly fast. With a basic node.js application, you can create your own web-server (which means that you don't need to use the likes of Apache or Nginx, and thus server your content in a much speedier fashion. And we add socket.io to the mix and what you have is a great solution to create apps that work in every browser and every mobile device. Socket.io allows you to push messages back and forth between a "client" (your browser, device, etc) and the "server" (that is the websocket server invoked by socket.io) and henceforth the most basic application to create with socket.io is chat, where every person who connects to the client will be able to push messages to the server and in term the server will push the reply/replies back to all connected client - an ideal chat solution isn't it?

Here's a bullet point list of the main features for the application I'm going to showcase:

  • People can enter their names in the chat
  • They are then connected to the websocket server
  • The websocket server sends a confirmation message to the client and also notifies other connected clients that someone else has joined
  • Users are able to exchange messages
  • Connected clients are notified if a person leaves the chat
  • Connected clients are notified if the websocket server shuts down

Pretty standard, nothing out of order here. Let's start coding!

First we'll be creating a websocket server that will accept connections on IP "1.2.3.4" port 1223. We are also going to create a hash to store the list of connected users:

var io = require("socket.io");  
var socket = io.listen(1223, "1.2.3.4");  
var people = {};  

Every action that you want to happen while the websocket server is running has to be between a special function: socket.on("connection", function(client) {...});, so our "actions" are:

  • join
  • send
  • disconnect

To learn more about the exposed events in socket.io (both server and client side), please see the following page on GitHub.

Let's develop our previously mentioned 3 "actions":

socket.on("connection", function (client) {  
    client.on("join", function(name){
        people[client.id] = name;
        client.emit("update", "You have connected to the server.");
        socket.sockets.emit("update", name + " has joined the server.")
        socket.sockets.emit("update-people", people);
    });

    client.on("send", function(msg){
        socket.sockets.emit("chat", people[client.id], msg);
    });

    client.on("disconnect", function(){
        socket.sockets.emit("update", people[client.id] + " has left the server.");
        delete people[client.id];
        socket.sockets.emit("update-people", people);
    });
});

The websocket server will "listen" to a "join" function, which will be emitted by the connected clients (and as we will see it's called via a button click event). The join function will send a 'name' parameter that is then added to the people hash, with a unique key index - which is the unique ID of the connected socket. Once this is saved, the websocket will emit a message ("update") which our client will be listening for - please note that the client.emit() will only update the client that you are looking at whereas socket.sockets.emit() will update all connected clients. In other words, the "You have connected to the server" message will only appear for the currently connected person, whereas the other two emits will push their messages to everyone including the currently connected person.

The next function is the "send" function, which will emit a message to all the connected clients, and send the person's details (name) as well as the actual message over the wire.

Disconnect - does what it supposed to, it'll emit a message to all connected clients letting them no know that someone has disconnected, delete the correct person from the people hash and update the list of people connected.

Let's have a look at the client side of things now - first the html:

<!DOCTYPE html>  
<html lang="en">
<head>
    <script src="http://1.2.3.4:1223/socket.io/socket.io.js"></script>
    <script src="http://1.2.3.4/js/bootstrap.js"></script>
    <script src="http://1.2.3.4/js/jquery.js"></script>
    <link rel="stylesheet" type="text/css" href="http://1.2.3.4/css/bootstrap.css">
    <script>
        <!-- We will add this later -->
        </script>
<body>

<div class="row">
  <div class="span2">
      <ul id="people" class="unstyled"></ul>
  </div>
  <div class="span4">
      <ul id="msgs" class="unstyled">
  </div>
</div>
<div class="row">
    <div class="span5 offset2" id="login">
        <form class="form-inline">
              <input type="text" class="input-small" placeholder="Your name" id="name">
            <input type="button" name="join" id="join" value="Join" class="btn btn-primary">
        </form>
    </div>

    <div class="span5 offset2" id="chat">
        <form id="2" class="form-inline">
              <input type="text" class="input" placeholder="Your message" id="msg">
            <input type="button" name="send" id="send" value="Send" class="btn btn-success">
        </form>
    </div>
  </div>
</div>

</body>
</html>

There are no surprising things here, it's a simple HTML page utilising the Bootstrap library.
And now the JavaScript - insert this between the script tag:

$(document).ready(function(){  
        var socket = io.connect("1.2.3.4:1223");
        $("#chat").hide();
        $("#name").focus();
        $("form").submit(function(event){
            event.preventDefault();
        });

        $("#join").click(function(){
            var name = $("#name").val();
            if (name != "") {
                socket.emit("join", name);
                $("#login").detach();
                $("#chat").show();
                $("#msg").focus();
                ready = true;
            }
        });

        $("#name").keypress(function(e){
            if(e.which == 13) {
                var name = $("#name").val();
                if (name != "") {
                    socket.emit("join", name);
                    ready = true;
                    $("#login").detach();
                    $("#chat").show();
                    $("#msg").focus();
                }
            }
        });

        socket.on("update", function(msg) {
            if(ready)
                $("#msgs").append("
  • " + msg + "
  • "); }) socket.on("update-people", function(people){ if(ready) { $("#people").empty(); $.each(people, function(clientid, name) { $('#people').append("
  • " + name + "
  • "); }); } }); socket.on("chat", function(who, msg){ if(ready) { $("#msgs").append("
  • " + who + " says: " + msg + "
  • "); } }); socket.on("disconnect", function(){ $("#msgs").append("
  • The server is not available
  • "); $("#msg").attr("disabled", "disabled"); $("#send").attr("disabled", "disabled"); }); $("#send").click(function(){ var msg = $("#msg").val(); socket.emit("send", msg); $("#msg").val(""); }); $("#msg").keypress(function(e){ if(e.which == 13) { var msg = $("#msg").val(); socket.emit("send", msg); $("#msg").val(""); } }); });

    Update!
    The code above works if you are running it from an already existing server, however if you want to execute the code without having to install/run Apache (or any other server for that matter) you can quickly setup a server with node.js:

    var http = require(‘http’),  
    fs = require(‘fs’),  
    io = require(‘socket.io’),  
    index;  
    fs.readFile(‘./chat.html’, function (err, data) {  
     if (err) {
        throw err;
     }
     index = data;
    });
    var server = http.createServer(function(request, response) {  
      response.writeHeader(200, {“Content-Type”: “text/html”});
      response.write(index);
      response.end();
    }).listen(1223);
    //and replace var socket = io.listen(1223, "1.2.3.4"); with:
    var socket = io.listen(server);  
    

    We have a lot more to discuss here as opposed to the HTML. First of all we connect to the running websocket (this is the bit where I have to mention, please make sure to run node chat.js), and hide a few elements using standard jQuery functions. Then we have a function for the join button, (#join) and if the name is properly filled out, we emit the join message to the websocket server. (Remember, we have a piece of code on the server side that will process the contents sent via 'join'). The update and update-people functions are both emitted by the server and on the client side we just update a few lists.

    Finally the chat function - that is emitted from the server again - allows us to add the actual messages sent from every person (client) to the server and display the messages.

    To explain this better - and if you understand this, you got the fundamentals of how websockets communicate with the client - let me copy-paste the code from the server and the client here in the logical order of processing (let's assume that the websocket server is running and that the client is already connected to the server):

    $("#send").click(function() {  
            socket.emit("send", msg);
    }
    

    If the user clicks the 'send' button, we emit the "send" message to the server and pass on the actual message as an argument.

    client.on("send", function(msg){  
        socket.sockets.emit("chat", people[client.id], msg);
    });
    

    On the server side we are constantly listening to the "send" message to arrive, once it does, we emit a "chat" message to all the connected clients, and pass the message and username of the person who wrote that message as parameters.

    socket.on("chat", function(who, msg){  
        $("#msgs").append("<li><strong><span class='text-success'>" + who + "</span></strong> says: " + msg + "</li>");
    });
    

    We are back to the client again, as we need to process the server's recently emitted "chat" message, which has two parameters, so we can simply use those and print them out on the screen - in this case as part of a <div>.

    I hope you liked this tutorial, please feel free to check out the code from GitHub.

    Show Comments