Developing Web Applications Using Node.js Part 2

Đã đăng vào - Cập Nhật Lần Cuối vào

Node.js is great for developing real-time applications. In this article, I will present a real-time chat server, implemented using node.js and socket.io on the server side. On the UI side, I used Bootstrap (with United theme) and the socket.io client side script, plus jQuery.

The code for the chat application is hosted on GitHub under the node_projects repository. Before running the application, all the dependencies have to be installed. This can be done using:

    greg@earth:~/Development/_freelancer/git/node_projects/node_chat$ sudo npm install

After the installation, the server can be started using:

    greg@earth:~/Development/_freelancer/git/node_projects/node_chat$ node server.js 
        Started node_chat, listening on port 8989.
        socket io is listening for users.

Locally, I can use two browsers to simulate a chat between two users:

John's chat window

Inside Chrome, I can see the following:

Bob's chat window

When users are chatting, the application displays the messages and highlights the usernames:

The Node Server

The node server uses the http, fs, path, mime and socket.io modules. The mime and socket.io are external modules, and these have to be included in the packages.json file:

{
  "name": "node_chat",
  "version": "0.0.1",
  "description": "Simple chat app using node.js",
  "dependencies": {
    "socket.io": "~1.0.6",
    "mime": "~1.3.4"
  }
}

Since node is a low-level framework, a lot of things had to be handled by myself rather than by the framework. For example, serving static files to the client.

var server = http.createServer(function(request, response){
        var filePath = false;
 
        if(request.url == "/") {
                filePath = "public/index.html";
        }
        else {
                filePath = "public" + request.url;
        }
 
        var fullPath = './' + filePath;
        sendStaticFile(response, local_cache, fullPath);
 
});
 
server.listen(8989, function() {
        console.log("Started node_chat, listening on port 8989.");
        chat_server.listenForUsers(server);
});

The http.createServer() method was already covered in the first part. The server is listening on port 8989 and in the callback method, I start listening for users with the chat_server (a socket.io based server) node module.

In the method sendStaticFile() I check if the requested file is in the cache or not. If it is in the cache, then I return it from the cache. Otherwise, I look it up in the file system. If the requested file is not found, then I use the sendPageNotFound() method to return a HTTP 404 response to the client. If the file exists, I load it, cache it and finally return its content.

function sendStaticFile(response, cache, path) {
        if(cache[path]) {
           sendFile(response, path, cache[path]);
        }
        else {
           fs.exists(path, function(exists){
                 if(exists) {
                      fs.readFile(path, function(error, content){
                             if(error) {
                                   console.log(error);
                                   sendPageNotFound(response);
                             }
                             else {
                                   cache[path] = content;
                                   sendFile(response, path, content);
                             }
                      });
                 }
                 else {
                      console.log("File at:" + path + " not found.");
                      sendPageNotFound(response);
                 }
           });
        }
}

The chat_server module takes a HTTP server as argument. Socket.io uses this server instance to listen for new connections. Once a connection request arrives a new username is assigned (assignUserName) then I add the new user to the default chat room (Lounge). After that, I subscribe to message types coming on the wire. These subscriptions and message handlers are implemented in the methods handleMessageBroadcast(), handleNameChangingRequest() and handleChatRoomJoiningRequest()

exports.listenForUsers = function(server) {
         console.log("socket io is listening for users.");
        io = socketio.listen(server);
        
        io.sockets.on('connection', function(socket){
            userCounter = assignUserName(socket, userCounter, userNames, usedUserNames); 
            handleChatRoomJoining(socket, 'Lounge'); 
            handleMessageBroadcast(socket, userNames); 
            handleNameChangingRequests(socket, userNames, usedUserNames); 
            handleChatRoomJoiningRequest(socket); 
            socket.on('chat_rooms', function(){
                    socket.emit('chat_rooms', chat_rooms);
            });
        });
};

The handleMessageBroadcast() is the method that sends the messages between the clients. It subscribes to the “message” request coming from clients, and it broadcasts the received message and the sender to the other connections as a JSON object.

function handleMessageBroadcast(socket, userNames) {
    socket.on("message", function(message){
        console.log("Recieved message: [" + userNames[socket.id] + ":"  + message.text + "]");
        socket.broadcast.to(message.room).emit("message",{
              from: userNames[socket.id],
              text:  message.text
        });
    });
}

The UI Code

On the UI side, I implemented a Chat class. This is used by socket.io's client side script to handle messages coming from the server and to update the UI accordingly. The JavaScript UI code is in the chat.js and chat_helper.js files.

For example, the message handling on the UI side is implemented in the chat_helper.js file:

socket.on("message", function(result){
      var newMessageElement = $("<div></div>");
      var userElement = $("<p></p>").html($("<b></b>").text("[" + result.from + "]: "));
      var textElement = $("<span></span>").text(result.text);
      $(userElement).append(textElement);
      $(newMessageElement).append(userElement);
 
      $("#messages").append(newMessageElement);
});

Using jQuery, I create new HTML element, then I assign the message text coming from the server and display the element in the #messages div element.

As you can see, using Node for Web development takes more effort, mainly because more code has to be written. But there are a lot of modules in the Node Package Manager (accessible through npm) that can help use the features that have already been implemented by other developers.

Đã đăng 2 tháng 3, 2015

Greg Bogdan

Software Engineer, Blogger, Tech Enthusiast

I am a Software Engineer with over 7 years of experience in different domains(ERP, Financial Products and Alerting Systems). My main expertise is .NET, Java, Python and JavaScript. I like technical writing and have good experience in creating tutorials and how to technical articles. I am passionate about technology and I love what I do and I always intend to 100% fulfill the project which I am ...

Bài báo kế tiếp

Creating Effective Call to Action Buttons