Skip to main content

Chat 2.0 - an upgraded chat written in node.js and socket.io

3 min read

Older Article

This article was published 13 years ago. Some information may be outdated or no longer applicable.

I got some brilliant feedback on the original chat app built with node.js and socket.io. So I went back in, took your suggestions, and bolted on new features. Here’s what changed and how.

First, I killed the need for multiple ports. The socket.io backend and the ExpressJS frontend now share a single port, and everything lives in server.js. (The snippet below has been trimmed.)

var express = require('express');
var app = (module.exports = express());
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);

server.listen(3000, '192.168.56.101', function () {
  console.log('Express server up and running.');
});

Second, I added duplicate room prevention. Every chat room must have a unique name, enforced on creation.

On the client side (client.js), an emit statement checks whether the room name already exists:

$('#createRoomBtn').click(function () {
  var roomExists = false;
  var roomName = $('#createRoomName').val();
  socket.emit('check', roomName, function (data) {
    roomExists = data.result;
    if (roomExists) {
    } else {
      if (roomName.length > 0) {
        //also check for roomname
        socket.emit('createRoom', roomName);
        $('#createRoom').hide();
        $('#createRoomForm').hide();
      }
    }
  });
});

The backend (server.js) iterates through the rooms hash and checks the name property:

socket.on('check', function (name, fn) {
  var keys = Object.keys(rooms);
  var match = false;
  if (keys.length != 0) {
    for (var i = 0; i < keys.length; i++) {
      console.log(rooms[keys[i]].name);
      if (rooms[keys[i]].name === name) {
        console.log('matched room: ' + name);
        match = true;
        break;
      }
    }
  }
  console.log('match: ' + match);
  fn({ result: match });
});

The extra fn callback ensures the check finishes before the jQuery code on the client side continues executing.

Now for the fun bit: whisper mode. You can now send a private message to another person that only they can see. It’s still a beta feature and the syntax is a bit raw. You type w:USER:MESSAGE into the input box. The code parses that format, looks up the user, confirms they’re connected, and routes the message. If the lookup fails (or you’re not whispering), everything flows through the normal chat path.

socket.on('send', function (msg) {
  var re = /^[w]:.*:/;
  var whisper = re.test(msg);
  var whisperStr = msg.split(':');
  var found = false;
  if (whisper) {
    var whisperTo = whisperStr[1];
    var keys = Object.keys(people);
    if (keys.length != 0) {
      for (var i = 0; i < keys.length; i++) {
        if (people[keys[i]].name === whisperTo) {
          console.log('matched person');
          var whisperId = keys[i];
          found = true;
          if (socket.id === whisperId) {
            //can't whisper to ourselves
            socket.emit('update', "You can't whisper to yourself.");
          }
          break;
        }
      }
    }
    if (found && socket.id !== whisperId) {
      var whisperTo = whisperStr[1];
      var whisperMsg = whisperStr[2];
      socket.emit('whisper', { name: 'You' }, whisperMsg);
      io.sockets
        .socket(whisperId)
        .emit('whisper', people[socket.id], whisperMsg);
    } else {
      socket.emit('update', "Can't find " + whisperTo);
    }
  } else {
    if (
      io.sockets.manager.roomClients[socket.id]['/' + socket.room] !== undefined
    ) {
      io.sockets.in(socket.room).emit('chat', people[socket.id], msg);
    } else {
      socket.emit('update', 'Please connect to a room.');
    }
  }
});

I’ve also thrown in some styling. I’m not a designer (trust me on this), but the functionality holds up.

You can grab the whole codebase on GitHub.

To install: npm install && bower install. To launch: npm start.