Working with namespaces

In this section, we won't be adding any new functionality to our chat room, but instead we will just use a feature of socket.io to make our application design better and our code easier to maintain.

We are sending different messages between the client and the server and differentiating them by type. Wouldn't it be better that we send different messages on different messaging channels? Our current approach also doesn't play well and may cause conflicts when our application or module is part of a larger system. But then there are questions, what will be the cost of opening multiple connections? What will be the effect on performance?

This is where namespaces come to the rescue. A namespace provides a way to multiply a socket.io connection, so that we get different channels for different types of messages without adding a big overhead to the system and its performance. Let us see how we can use namespaces in our chat system.

In our chat application, we have two different types of messages or events being sent. These are infrastructural, such as setting the name and welcome messages, and communication between the users.

So let us go ahead and create two namespaces, namely chat_com and chat_infra. We will send the communication messages (user messages) on chat_com and the infrastructural messages (welcome, user entry, and so on) on chat_infra. For this, let us first edit the socket.js file, which is on the server:

var io = require('socket.io'),

exports.initialize = function (server) {
  io = io.listen(server);

  var chatInfra = io.of("/chat_infra")
      .on("connection", function(socket){
        socket.on("set_name", function (data) {
          socket.set('nickname', data.name, function () {
            socket.emit('name_set', data);
            socket.send(JSON.stringify({type:'serverMessage',
              message:'Welcome to the most interesting ' +
              'chat room on earth!'}));
            socket.broadcast.emit('user_entered', data);
          });
        });
      });

  var chatCom = io.of("/chat_com")
      .on("connection", function (
socket) {
        socket.on('message', function (message) {
          message = JSON.parse(message);
          if (message.type == "userMessage") {
            socket.get('nickname', function (err, nickname) {
              message.username = nickname;
              socket.broadcast.send(JSON.stringify(message));
              message.type = "myMessage";
              socket.send(JSON.stringify(message));
            });
          }
        });
      });
}

As we can see from the preceding code, most of the code remains the same, apart from the highlighted snippets and some code reorganization.

What we are doing here is separating the messages and events into two code blocks corresponding to their namespaces. We use the io.of method to create a new namespace. Upon creation of the namespace, it can be used as any socket's object.

In our case, we are creating two namespaces and adding a connection event handler to both of them. One for chat_infra, as shown in the following code snippet:

  var chatInfra = io.of("/chat_infra")
      .on("connection", function(socket){

And another for chat_com:

  var chatCom = io.of("/chat_com")
      .on("connection", function (socket) {

Once the connection is established, we will get a socket object in the connection event handler, which we will use just as we did earlier. In case of chat_infra, we add all the messaging and events that are not part of the user-to-user communication:

        socket.on("set_name", function (data) {
          socket.set('nickname', data.name, function () {
            socket.emit('name_set', data);
            socket.send(JSON.stringify({type:'serverMessage',
              message:'Welcome to the most interesting ' +
              'chat room on earth!'}));
            socket.broadcast.emit('user_entered', data);
          });
        });

So, we are moving the set_name handler, the event emitter for name_set, messaging for serverMessage, and the event broadcaster for user_entered to the chat_infra namespace.

        socket.on('message', function (message) {
          message = JSON.parse(message);
          if (message.type == "userMessage") {
            socket.get('nickname', function (err, nickname) {
              message.username = nickname;
              socket.broadcast.send(JSON.stringify(message));
              message.type = "myMessage";
              socket.send(JSON.stringify(message));
            });
          }
        });

This leaves only the standard User messaging components on the chat_com namespace.

Let us now see how this affects our client code:

var chatInfra = io.connect('/chat_infra'),
    chatCom = io.connect('/chat_com'),

chatInfra.on('name_set', function (data) {
  chatInfra.on("user_entered", function (user) {
    $('#messages').append('<div class="systemMessage">' + user.name
        + ' has joined the room.' + '</div>'),
  });

  chatInfra.on('message', function (message) {
    var message = JSON.parse(message);
    $('#messages').append('<div class="' + message.type + '">'
        + message.message + '</div>'),
  });

  chatCom.on('message', function (message) {
    var message = JSON.parse(message);
      $('#messages').append('<div class="' + 
    message.type + '"><span class="name">' +
     message.username + ':</span> ' +
           message.message + '</div>'),
  });

  $('#nameform').hide();
  $('#messages').append('<div class="systemMessage">Hello ' +
     data.name + '</div>'),

  $('#send').click(function () {
    var data = {
      message:$('#message').val(),
      type:'userMessage'
    };
    chatCom.send(JSON.stringify(data));
    $('#message').val(''),
  });
});

$(function () {
  $('#setname').click(function () {
    chatInfra.emit("set_name", {name:$('#nickname').val()});
  });
});

The first and the most important thing we see in the previous code is that we are connecting two sockets:

var chatInfra = io.connect('/chat_infra'),
    chatCom = io.connect('/chat_com'),

In fact, socket.io will establish a single socket connection and multiplex the two namespaces over it. But establishing these two connections will give us the ability to handle the chat_infra and chat_com namespaces' messages or events separately.

In the following code snippet, we are adding the handlers that correspond to the emitters for chat_infra that we added on the server. The name_set handler will be on the chat_infra namespace:

chatInfra.on('name_set', function (data) {

We will also do the same for the user_entered handler:

  chatInfra.on("user_entered", function (user) {
    $('#messages').append('<div class="systemMessage">' + user.name
        + ' has joined the room.' + '</div>'),
  });

Next, we add the on handler to listen for the messages on chat_infra; this will receive all the server messages:

  chatInfra.on('message', function (message) {
    var message = JSON.parse(message);
    $('#messages').append('<div class="' + message.type + '">'
        + message.message + '</div>'),
  });

We also emit the set_name event on chat_infra:

    chatInfra.emit("set_name", {name:$('#nickname').val()});

On the chat_com namespace, we send the user message, as shown in the following code:

$('#send').click(function () {
    var data = {
      message:$('#message').val(),
      type:'userMessage'
    };
    chatCom.send(JSON.stringify(data));

Also, we will attach the handler to receive the user messages relayed from the server by using the following code snippet:

  chatCom.on('message', function (message) {
    var message = JSON.parse(message);
      $('#messages').append('<div class="' + 
     message.type + '"><span class="name">' +
     message.username + ':</span> ' +
           message.message + '</div>'),
  });

Now that we understand namespaces and have made use of them to clean up our design and code, let us go ahead and add some new features.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset