This multi-room chat example is part 2 in the node.js socket.io chat series (previous chat tutorial).
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>
Thanks for the article, really helped
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
are you using a websockets enabled browser?
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!
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.
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
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
Hey, see the response I gave to Ed above.
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.
Sure – redis is/can be persistent, so upon server crash, you’ll still have your data preserved.
HI how to manage two rooms at time .Means same window create a tab dynamically based room .
there is something called groups in socket.io, check those out
Sorry i don’t understand.Plz tell me brief explanation .
https://github.com/LearnBoost/socket.io
Search for “Rooms” everything is documented.
the article is really helpful…tq..
will you please give some example to use redis store instead of socket store..
I’ll see if I can, but if you’d be interested in getting started, I use https://github.com/mranney/node_redis for personal projects.. as well as https://github.com/visionmedia/connect-redis
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?
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.
why was express used?
Easy to learn and demo with.
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+”);
});
});
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 ?
check location of JS script on front end, should be remote, not localhost
What do you mean JS Script on front end?
Most likely where io.connect is being passed “localhost:8080″ .. should be “yourdomain.com:8080″
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
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);
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.
Not sure, taking a while to establish a socket connection?
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?
jQuery should be loading, are you using it after the dom is ready? Add more scripts with the same
Thank you so much for this tutorial, it helps a lot! cheers mate
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);
Sorry about the ridiculous number of comments I left… but the latest one is correct. Please delete all others.
@ Justin liked your modification code
can you send me the snip you used in
the switch room section like above
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!
Thank you for a really good tutorial!
is it possible to get notifications for all rooms e.g. join 2-3 rooms at the same time and get notified every time someone messages in one or more rooms u r currently in?
Thanks this is sweet. both tutorials worked perfect. I have been wanting to mess with web sockets for a while and your tutorials have really helped me to start learning them.
Hi, Thank for the awosome tutorial. Everything work. What I have to do now is to create the dynamic room.
please delete this and above comment. As I came to know that This has already dynamic room feature.
Hi Psits, thanks for this amazing tutorial. i’m curious, I want to give users the ability to create chat rooms instead of pre-defining them from the server app.js , i’d like the users to create rooms from client side. Thanks
Hi,
Thanks for sharing of your code, but i only have one question.. I wrote a code to let users join multiple rooms, but there is one problem with messages in the room.. How can i make it on best way if you have more rooms, but if you click one of your rooms, it shows up the sended messages of that choosed room?
Hi,
Thanks for the code.
But is it possible that multiple rooms shares a common users?
Thank for the code
It help me a lot.
i am using convers.js
i want help in retrieving those rooms user is currently involved.?
i.e. i need rooms that user has joined and is currently a part of conversation in that room.?
help me with javascript code for this.
thanking you in advance
Lots of thanks for the article. Helped me a lot. Thanks.
// update list of users in chat, client-side
io.sockets.emit(‘updateusers’, usernames);
where realization?