Skip to content

node.js and socket.io multiroom chat tutorial

i’ve been meaning to update the previous chat tutorial to include multiple rooms, here it is.

what i failed to cover in the previous example was how to send messages/broadcast information with socket.io. the following examples assume you define the module:

var io = require('socket.io').listen(app);
io.sockets.on('connection', function (socket) { .... });

from the server, emit to the client’s socket only, using the socket callback:

socket.emit('function', 'data1', 'data2');

from the server, emit to everyone but the client’s socket (to broadcast a message):

socket.broadcast.emit('function', 'data1', 'data2');

from the server, emit globally:

io.sockets.emit('function', 'data1', 'data2');

you see that io was initially defined as the socket.io module, so we emit to all its sockets.

one of the latest updates to socket.io now allows rooms/groups. for a client to join and leave a room:

socket.join('room1');
socket.leave('room1');

to broadcast information to a certain room (excluding the client):

socket.broadcast.to('room1').emit('function', 'data1', 'data2');

to broadcast information globally:

io.sockets.in('room1').emit('function', 'data1', 'data2');

on with the tutorial, in the new app.js, when a user connects and enters their username, he joins room1 by default and broadcasts (only to room1) that they’ve joined. i’ve also added a list of available rooms (room1, room2, room3) and a function that updates that list to reflect the user’s current room.

if the user decides to switch rooms, a client-side function switchRoom is called. the server listens for this call and tells the user’s socket to leave the current room and join the new room, broadcasts locations and updates the room list.

here is the updated app.js:

var app = require('express').createServer()
var io = require('socket.io').listen(app);

app.listen(8080);

// routing
app.get('/', function (req, res) {
  res.sendfile(__dirname + '/index.html');
});

// usernames which are currently connected to the chat
var usernames = {};

// rooms which are currently available in chat
var rooms = ['room1','room2','room3'];

io.sockets.on('connection', function (socket) {

	// when the client emits 'adduser', this listens and executes
	socket.on('adduser', function(username){
		// store the username in the socket session for this client
		socket.username = username;
		// store the room name in the socket session for this client
		socket.room = 'room1';
		// add the client's username to the global list
		usernames[username] = username;
		// send client to room 1
		socket.join('room1');
		// echo to client they've connected
		socket.emit('updatechat', 'SERVER', 'you have connected to room1');
		// echo to room 1 that a person has connected to their room
		socket.broadcast.to('room1').emit('updatechat', 'SERVER', username + ' has connected to this room');
		socket.emit('updaterooms', rooms, 'room1');
	});

	// when the client emits 'sendchat', this listens and executes
	socket.on('sendchat', function (data) {
		// we tell the client to execute 'updatechat' with 2 parameters
		io.sockets.in(socket.room).emit('updatechat', socket.username, data);
	});

	socket.on('switchRoom', function(newroom){
		// leave the current room (stored in session)
		socket.leave(socket.room);
		// join new room, received as function parameter
		socket.join(newroom);
		socket.emit('updatechat', 'SERVER', 'you have connected to '+ newroom);
		// sent message to OLD room
		socket.broadcast.to(socket.room).emit('updatechat', 'SERVER', socket.username+' has left this room');
		// update socket session room title
		socket.room = newroom;
		socket.broadcast.to(newroom).emit('updatechat', 'SERVER', socket.username+' has joined this room');
		socket.emit('updaterooms', rooms, newroom);
	});

	// when the user disconnects.. perform this
	socket.on('disconnect', function(){
		// remove the username from global usernames list
		delete usernames[socket.username];
		// update list of users in chat, client-side
		io.sockets.emit('updateusers', usernames);
		// echo globally that this client has left
		socket.broadcast.emit('updatechat', 'SERVER', socket.username + ' has disconnected');
		socket.leave(socket.room);
	});
});

here is the updated index.html:

<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script>
	var socket = io.connect('http://localhost:8080');

	// on connection to server, ask for user's name with an anonymous callback
	socket.on('connect', function(){
		// call the server-side function 'adduser' and send one parameter (value of prompt)
		socket.emit('adduser', prompt("What's your name?"));
	});

	// listener, whenever the server emits 'updatechat', this updates the chat body
	socket.on('updatechat', function (username, data) {
		$('#conversation').append('<b>'+username + ':</b> ' + data + '<br>');
	});

	// listener, whenever the server emits 'updaterooms', this updates the room the client is in
	socket.on('updaterooms', function(rooms, current_room) {
		$('#rooms').empty();
		$.each(rooms, function(key, value) {
			if(value == current_room){
				$('#rooms').append('<div>' + value + '</div>');
			}
			else {
				$('#rooms').append('<div><a href="#" onclick="switchRoom(\''+value+'\')">' + value + '</a></div>');
			}
		});
	});

	function switchRoom(room){
		socket.emit('switchRoom', room);
	}

	// on load of page
	$(function(){
		// when the client clicks SEND
		$('#datasend').click( function() {
			var message = $('#data').val();
			$('#data').val('');
			// tell server to execute 'sendchat' and send along one parameter
			socket.emit('sendchat', message);
		});

		// when the client hits ENTER on their keyboard
		$('#data').keypress(function(e) {
			if(e.which == 13) {
				$(this).blur();
				$('#datasend').focus().click();
			}
		});
	});

</script>
<div style="float:left;width:100px;border-right:1px solid black;height:300px;padding:10px;overflow:scroll-y;">
	<b>ROOMS</b>
	<div id="rooms"></div>
</div>
<div style="float:left;width:300px;height:250px;overflow:scroll-y;padding:10px;">
	<div id="conversation"></div>
	<input id="data" style="width:200px;" />
	<input type="button" id="datasend" value="send" />
</div>

GIT repo https://github.com/mmukhin/psitsmike_example_2

Comment Feed

40 Responses

  1. Thanks for the article, really helped

  2. Thanks for the article, really helped.But
    iam run your code getting below error
    warn – client not handshaken client should reconnect

    how to increase socket client connection time

  3. Great tutorial. Is it possible to show an example of getting the connected user(s) usernames and id’s from a particular channel? I noticed io.socket.clients(‘channel_name’) holds an array of this but for the life of me can’t figure out how to display it. Thank you for all your help!

    • michael mukhinFebruary 8, 2012 @ 12:52 amReply

      Good question, I remember reading about this somewhere.. unfortunately I don’t remember off the top of my head. I can investigate later (currently writing mongodb tut), but if no such function is available it is possible to store ids in a hash table in redis (on each USER connect, do something like this pseudocode redis.rooms_users.room1.push(USER_ID) and then you’re able to see all the users youve stored there, and obviously removed on disconnect) or document mongo – the former is much better.

  4. how do i get dynamic room client count beacause ineed to create new room if room filled .
    iam trying get room cout iam failed.plz help me

  5. Hi michael mukhin
    how do i get dynamic room client count beacause ineed to create new room if room filled .
    Iam trying get room cout iam failed.plz help me

  6. Alright yea that makes sense. I don’t know much about redis so I’ll look into that. For now I am creating another array and saving the data in json format for each user, then to get the connected users for the requested channel I’m iterating through the array and if (channel == match) output. Then simply removing the user from the array on disconnect. It’s sloppy but works for right now until I can figure out redis or io.sockets.clients() starts making more sense to people. Thanks again for your help Mike.

    • michael mukhinFebruary 9, 2012 @ 9:25 amReply

      Sure – redis is/can be persistent, so upon server crash, you’ll still have your data preserved.

  7. HI how to manage two rooms at time .Means same window create a tab dynamically based room .

  8. Sorry i don’t understand.Plz tell me brief explanation .

  9. the article is really helpful…tq..
    will you please give some example to use redis store instead of socket store..

  10. It works well in firefox and chromium, but it does not work in opera at all. What can be the problem? Is it because opera uses flash for sockets?

    • michael mukhinMarch 26, 2012 @ 7:41 pmReply

      Hmm, did not test in Opera. Socket.io should fallback to other methods if websockets are not enabled – http://socket.io/#browser-support

      I’ll take a look as well.

      • After some googling I found many reports that socket.io works improperly in opera. Can you solve the problem?

      • Actually it works in opera. But if I delete this part
        // routing
        app.get(‘/’, function (req, res) {
        res.sendfile(__dirname + ‘/index.html’);
        });

        and start index.html from apache it stops working in opera, however there is no problem in firefox and chromium.

  11. why was express used?

  12. JustinMay 16, 2012 @ 8:21 amReply

    Here is some code I made to list out the users only in each room. I know some comments above were looking for something like this. Hope this helps.

    I use this in the adduser function of app.js and a modified version in the switchRoom function (to update the lists as people enter and leave rooms) and another modified version in the disconnect function to remove people from the room user list as they leave the server.

    //app.js code
    // create an array to hold all the usernames of the poeple in a specific room
    var roomusers = [];
    // get all the clients in ‘room1′
    var clients = io.sockets.clients(‘room1′);
    // loop through clients in ‘room1′ and add their usernames to the roomusers array
    for(var i = 0; i < clients.length; i++) {
    roomusers[roomusers.length] = clients[i].username;
    }
    // broadcast to everyone in room 1 the usernames of the clients connected.
    io.sockets.to('room1').emit('updateroomusers',roomusers);

    //index.html code
    socket.on('updateroomusers', function(roomusers) {
    $("#roomusers").empty();
    $.each(roomusers, function (key, value) {
    $('#roomusers').append('’+value+”);
    });
    });

  13. It was working great in my localhost, but when i run this example on a public IP server, i notice that the prompt() window is not displayed.
    Is there any issue with the web sockets ?

  14. Michael AAugust 5, 2012 @ 10:56 amReply

    Hey Michael,

    Thanks for the awesome tutorial! I have had some trouble because Express changed their syntax in the latest update, so it would be cool if you updated this to reflect the latest changes in the Express Framework!

    Thanks,
    Michael A

    • michael mukhinAugust 22, 2012 @ 6:29 pmReply

      Would you be able to point me to what changed? Sorry, I havent been following.

      • Express migrated from 2.x to 3.x

        In app.js replace the first few lines with:

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

        server.listen(8080);

  15. Why does it take so long to get the prompt for username? When I connect to index.html it takes almost 10 seconds before the prompt. Is it just me or what? Thanks for the example by the way.

  16. How do we get other javascript files to work with this system? For example, you have a script tag pointing to jquery, yet it does not load (I did a simple jquery hide command and nothing). Say we had a script that need to be loaded from a file like ‘player.js’, how do we get that served in this system?

    • michael mukhinAugust 22, 2012 @ 6:31 pmReply

      jQuery should be loading, are you using it after the dom is ready? Add more scripts with the same

  17. Thank you so much for this tutorial, it helps a lot! cheers mate

  18. Installed the latest version of socket.io and express, I get this:
    “TypeError: Arguments to path.join must be strings”

    • update package.json to reflect the updates. so express should be 3.1.0 and socket.io should be 0.9.14

      then run npm install (to get the latest dependencies)

      lastly, express 3.x requires a different syntax for starting the server than 2.x so change the beginning of app.js to this…

      var express = require(‘express’);
      var app = express()
      , http = require(‘http’)
      , server = http.createServer(app)
      , io = require(‘socket.io’).listen(server);

      server.listen(8080);

  19. Sorry about the ridiculous number of comments I left… but the latest one is correct. Please delete all others.

  20. @ Justin liked your modification code
    can you send me the snip you used in
    the switch room section like above

  21. Thank you. Spent the past week learning node.js through tutorials and books, but this update from your prior tutorial is such a nice example of how the pieces work together. Much appreciated!



Some HTML is OK

or, reply to this post via trackback.

CAPTCHA Image
*