1

I'm developing a simple chat app using node.js and express.io

I would like to display a list of the connected clients (or online for chat) all the time.

In express.io's doc, there is no clear way on how to "get" the list of connected clients once a new one has entered the room, i.e there is just the "broadcast" but not the "get".

Have someone done this before?

Any clues will be really helpful.

Thanks!

Edit:

After trying @jibsales's answer. I think we are almost there. What clients returns me is not the actual array of clients but this one:

[ { id: 'OWix3sqoFZAa20NLk304',
namespace: 
 { manager: [Object],
   name: '',
   sockets: [Object],
   auth: false,
   flags: [Object],
   _events: [Object] },
manager: 
 { server: [Object],
   namespaces: [Object],
   sockets: [Object],
   _events: [Object],
   settings: [Object],
   handshaken: [Object],
   connected: [Object],
   open: [Object],
   closed: [Object],
   rooms: [Object],
   roomClients: [Object],
   oldListeners: [Object],
   sequenceNumber: 496205112,
   router: [Object],
   middleware: [],
   route: [Function],
   use: [Function],
   broadcast: [Function],
   room: [Function],
   gc: [Object] },
disconnected: false,
ackPackets: 0,
acks: {},
flags: { endpoint: '', room: '' },
readable: true,
store: { store: [Object], id: 'OWix3sqoFZAa20NLk304', data: {} },
_events: 
 { error: [Function],
   ready: [Function],
   connection: [Function],
   NewChatPrivateLine: [Function],
   NewIdea: [Function],
   NewChatLine: [Function],
   NewPost: [Function] } } ]

The functions are:

var app = require('express.io')();
app.io.route('connection', function(req) {
  req.io.join(req.data.room);
  var clients = app.io.sockets.clients(req.data.room);
  console.log(clients)
  app.io.room(req.data.room).broadcast('announce', {
    user: req.data.user,
    clients: clients
  }) 
});

This actually returns an error ( data = JSON.stringify(ev); TypeError: Converting circular structure to JSON) as the array has several circular objects and hence it cannot be broadcasted.

Any thoughts?

user2988332
  • 115
  • 1
  • 9
  • You could always just keep track of them as they connect? – Brad Feb 10 '14 at 17:10
  • I was trying something like that, but the result was extremely complex for something that I think could be solved in a simpler and elegant way. Do you have an idea of code to keep track? – user2988332 Feb 10 '14 at 17:13

2 Answers2

2

Because express.io is simply glueing express and socket.io, don't forget to look at socket.io's documentation as well. With that said, since socket.io v0.7, we now have an API method to get this information:

var clients = io.sockets.clients('room'); // all users from room `room`

Unfortunately express.io is written in coffeescript (UGGGH!!!) so I'm having a hard time reading the source, but it looks like when you require the express.io module, the socket.io instance is hoisted up as well:

var express = require('express.io');
var clients = express.io.sockets.clients('room'); // all users from room `room`

If this doesn't work, I would ditch express.io for a manual configuration with express and socket.io because it looks like express.io has a VERY opinionated API. Its really not that hard at all as express.io is doing nothing more than creating a pretty interface/abstraction to manual configuration (which is actually hurting you in this use case if the above doesn't work).

I would also checkout SockJS as I (and many other websocket consumers) ditched socket.io for SockJS due to a lack of community support. Not to mention, there is a SEVERE memory leak in IE9 when falling back to xhr-polling.

srquinn
  • 10,134
  • 2
  • 48
  • 54
  • Hi @jibsales, thanks. I have tried it but it is displaying the following: var clients = req.io.sockets.clients(req.data.room); ^ TypeError: Cannot call method 'clients' of undefined I tried other combinations, e.g: req.io.room(req.data.room).clients() with no success. Any idea or site where I can read further? – user2988332 Feb 10 '14 at 17:59
  • Updated the questions, any thoughts? I'm really lost now! – user2988332 Feb 10 '14 at 21:01
  • How many clients do you have connected when it dumps that array? – srquinn Feb 10 '14 at 21:07
  • Only one (me) before the app is killed by the error. I tried inverting the order for the req.io.join(req.data.room); and the error came with in the second client connected. The first one returned an empty array. – user2988332 Feb 10 '14 at 21:13
  • Although var clients = app.io.sockets.clients(req.data.room).length returns the right number of connected clients. – user2988332 Feb 10 '14 at 21:21
  • Right – the error simply comes from trying to stringify that huge circular object literal. The length property will give you the amount of clients if you simply need a count. The nice thing about the huge circular object is that if you know the Index in the clients array, you can easily get a reference to that complete client and all of its properties. I argue that if you are trying to send over the entire object literal as a JSON response, you need to rethink your HTTP req/res cycles and edit down the object so you are only sending the necessary data across the line. – srquinn Feb 10 '14 at 21:26
  • Plus, most of that data can be found in the client side socket as well so there is no need to send it across the line unless you have a very unique use case in which you need to do so. I'm guessing your "simple chat client" doesn't need this. – srquinn Feb 10 '14 at 21:30
  • You're right. I'm trying to "catch" that index and maybe create an array with the nicknames of the connected clients. The problem being, I don't find the index anywhere.. :-/ – user2988332 Feb 10 '14 at 21:47
1

Well, finally I went with the "tacking" solution proposed by @Brad. It is not the most elegant but, if you can help me improve it, It'd be awesome!!

This is the final code:

Server-side

var app = require('express.io')();

//To broadcast the users online in room sent by the client
var clients = [];
app.io.route('connect', function (req) {
  req.io.leave(req.data.room).on('disconnect', function() {
    //To remove client from list when disconnected
    var index = clients.indexOf(req.data.user);
    if (index > -1) {
      clients.splice(index, 1);
    }
    app.io.room(req.data.room).broadcast('announce', {
      user: req.data.user,
      clients: clients
    }) 
  });
  req.io.join(req.data.room);
  //To avoid repeating the same client with several opened windows/tabs
  var index = clients.indexOf(req.data.user);
  if (index === -1) {
    clients.push(req.data.user); 
  }
  app.io.room(req.data.room).broadcast('announce', {
    user: req.data.user,
    clients: clients
  }) 
});

Client-side

// Emit ready event with person name and predefined room for who's online
io.emit('connect', {
  room: room,
  user: user
});

//Get the signal from server and create your list
io.on('announce', function (data){
 //Do awesome stuff with data 
}); 
user2988332
  • 115
  • 1
  • 9