Socket IO: Number of users in a room - node.js

Using Socket.IO v.1.3.5 for node.JS...
How can I get the number of real users (not sessions) that are connected in a room?
Number of sessions:
var room = io.sockets.adapter.rooms[roomId] || {};
var numSocketsInRoom = Object.keys(room).length;
But if the same user is connected in multiple browser tabs, the numSocketInRoom is increased because is the number of sessions, and not the real number of users.
In my case, any socket have the user saved in socket.userId, but I don't know how indicate this to get the number of users connected in a room...
Thank you!

My solution finally was this code, this code is executed after join (isConnect = true) or leave (isConnect = false) the room:
//Get the room:
var room = io.sockets.adapter.rooms[roomId] || {};
//Declaration of number of users variable
var numUsers;
//If there are any user connected in the room, create the object
if(room.user===undefined)
room.user = {};
//If you join the room and have another opened session, increase the number of sessions
if(room.user[socket.request.user.id] && isConnect)
room.user[socket.request.user.id] += 1;
//If you leave the room and have another opened session, decrease the number of sessions
else if(room.user[socket.request.user.id] && !isConnect){
room.user[socket.request.user.id] -= 1;
//If the number of sessions is 0, delete the user from the object
if(room.user[socket.request.user.id]===0)
delete room.user[socket.request.user.id];
}
//If the user don't have any session and join the room, put 1 session in the object
else if(isConnect){
room.user[socket.request.user.id] = 1;
}
//Get the number of users from room.user object
numUsers = Object.keys(room.user).length;

Related

how does a user connected to a room using socket.io get the data that was send to the room before he joined it

a user joins a room and emits its username to the room using passport.socketio . With the help of socket.request.user.user_id i am able to get the user_id of the socket and fetch the username corresponding to user_id from the database.
function onNewplayer_GK (data) {
var room_id = find_Roomid_GK();
var room = room_List_GK[room_id];
//join the room
this.room_id = room_id;
this.join(this.room_id);
console.log("created new room with id " + room_id);
console.log("created new player with id " + this.id);
var user_id = this.request.user.user_id;
db.collection('user-details').findOne({_id:ObjectId(user_id)},(err,result)=>{
var results = JSON.stringify(result);
var results = JSON.parse(results);
console.log('name is'+ (results.user_name));
var name = results.user_name;
this.emit('myname',{
my_name:name
});
this.broadcast.to(room_id).emit('enemyname',{
enemy_name:name
});
});
io.on('connection',(socket)=>{
console.log('user connected ' + socket.id);
// listen for new player
// socket.on("new_player", onNewplayer);
socket.on('new_player_history',onNewplayer_history);
socket.on('new_player_GK',onNewplayer_GK);
find_Roomid_GK() is a function which looks for rooms which are not filled and if filled it will create a room and assigns it to room_id.There is no problem in the functionality of the rooms. I just want to emit both the user's usernames to each other. how?? I am unable to emit the first user connected username to the second user connected after the first.
But when another user connects and joins the same room, it is able to emit its details to the connected sockets. But this socket is not able to get the details of the sockets connected before this socket which is reasonable enough to understand.
Does anyone have an idea on how this newly connected socket will fetch the username of the connected sockets using passport.socketio ?

NodeJs Socket programming how to handle and manage Sockets? (Without Using socket.io) What is the efficient way?

After connection to the Server, every time Data coming from this connection (Socket.on('data',...)), Server fetches UserID from Data and check the ClientList (array of Socket objects), to see if Socket with this UserID exists in ClientList, if not : adds UserID as a property of Socket and then adds Socket object to Client list.
So when user with ID=1 want to send a message to user with ID = 2,
Server search for Socket with UserID = 2 in ClientList to find the right Socket and send user 1's message to the found Socket (user 2's socket).
I'm trying to accomplish this without using socket.io! That's what my employer made me to do! :))
Now my question is: am I doing this right? Is this efficient to check ClientList array (every time a connection send Data) to see if this UserID exists in ClientList? if not, then what is the right and efficient way? there is no problem with my code and it works. but what if there are thousands of connections?
Any Sample code , example or link would be appreciated. Thank you.
here is a pseudo code :
var net = require('net');
var Server = net.createServer();
var myAuth = require('./myAuth');
var ClientList = [];
Server.on('connection', function(Socket){
Socket.UserData = {}; // I want to add user data as a property to Socket
Socket.on('data', function (Data) {
var userID = myAuth.Authenticate_and_getUserID(Data);
if(userID != undefined){
var found = false;
ClientList.filter(function(item){
// check if Socket is in ClientList
if(item.UserData.ID == userID){
// client was connected before
found = true;
}
});
if(!found){
// this is a new connection, Add it to ClientList
Socket.UserData.ID = userID;
ClientList.push(Socket);
}
}
Socket.once('close', function(has_error){
var index = ClientList.indexOf(Socket);
if (index != -1){
ClientList.splice(index, 1);
console.log('Client closed (port=' + Socket.remotePort + ').');
}
});
});
UPDATE for clarification:
is this efficient to look into ClientList every time Data is coming to Server, to check for receiverID (presence of receiver) and to Update ClientList with current connection UserID if not exists?
how should I manage new connections(users) and store them in server for later use when number of users are thousands or millions! NOT 10 or 100. How socket.io is doing this?
later usages could be:
check to see if one specific user is online (have an object in ClientList)
send realtime message to a user if he/she is online
etc . . .
Actually I am doing this wrong!
Arrays in JavaScript are passed by reference! So there is no need to update ClientList every time a Socket send data.
Therefor the Code changes like Following:
var net = require('net');
var Server = net.createServer();
var myAuth = require('./myAuth');
var ClientList = [];
Server.on('connection', function(Socket){
ClientList.push(Socket);
Socket._isAuthorized = false;
// when socket send data for the first time
// it gets authenticated and next time it send data
// server does not authenticate it
Socket.on('data', function (Data) {
var userID = getUserID(Data);
if(Socket._isAuthorized != true){
if(authenticate(Socket)){
Socket._isAuthorized = true;
Socket._userID = userID;
return;
}
}
// do something with data...
}
Socket.once('close', function(has_error){
var index = ClientList.indexOf(Socket);
if (index != -1){
ClientList.splice(index, 1);
console.log('Client closed (port=' + Socket.remotePort + ').');
}
});
});
And its efficient!

Best Approach to send real time analytics using socket.io

I am using socket.io and node.js/mongoDB for an app which will send real time analytics between Parents and Drivers
Let's say Driver is moving along a path and for every location change he will send his location to a list of specific parents.
I can think of one approach to achieve such functionality
1- I create two arrays
var userSockets = {};
var driverSockets = {};
Whenever a user/driver is connected i do
For Driver - driverSockets[accId] = socket
For User - userSockets[accId] = socket
Now if a driver has to emit a location change, he will do something like
userSockets[userId].emit(abc)
I would like to know if this approach is better? Would it be better to save users as onlineUsers in MongoDB but even then how to access their sockets to emit data.
What would be the best approach.
You should use "room" to store online user and emit to this room(channel) when location change.
//join a room(channel)
socket.join('online');
//sending to sender client, only if they are in 'online' room(channel)
socket.to('online').emit('location', {user_type:'driver'});
Here is the example code to seperate driver and parents in different array with user_type key-word and send driver location to all the parents in real time. Furthermore, We can add users mobile number to send specific driver location to a specific parent etc.
var parents = [];
var drivers = [];
var users = [];
io.on('connection', function(socket){
users.push(socket);
socket.on('disconnect', function(msg){
var i = users.indexOf(socket);
users.splice(i, 1);
var j = parents.indexOf(socket);
if(j !== -1){
parents.splice(j, 1);
}
var k = drivers.indexOf(socket);
if(k !== -1){
drivers.splice(k, 1);
}
});
socket.on('register', function(msg){
console.log(msg);//send json- {"user_type":"driver"}
var data = JSON.parse(msg);
var i = users.indexOf(socket);
if(data.user_type === 'driver'){
drivers.push(users[i]);
}else{
parents.push(users[i]);
}
users[i].emit('register', '{"status":"registered"}');
});
socket.on('location', function(msg){
console.log(msg);//send json- {"user_type":"driver","location":"lat,long"}
var data = JSON.parse(msg);
if(data.user_type === 'driver'){
for(var x=0;x<parents.length;x++){
parents[x].emit('location', '{"user_type":"driver", "location":"lat,long"}');
}
}
});
});

socket.io emit to specific user and give them x amount of time to respond

I have an online chat. It uses rooms. If a user sends message and the other user is not online, it should increment the "missed messages" counter. I tried to create a timeout with setTimeout and if they emit an event it clears that timeout.
However chatOnline doesn't fire as I expected to, which leads to it always reporting the user is offline and incrementing the counter for missed_texts column in rethinkdb (which is not shown because it isn't relevant).
How can I retrieve if the user is online from socket.io? My goal is to avoid having to store presence info in the database, which could get out of control quickly.
Code I tried:
socket.on('chatSend',function(data){
if(socket.client.user.room_id !== null){
//were storing some crap in the socket object for easy retrieval.
data.user_id = socket.client.user.id;
data.room_id = socket.client.user.room_id;
data.timestamp = ~~(new Date() / 1000);
//insert message into chat table
r.table('chat').insert(data).run().then(function(res){
//retrive generated record from table
r.table('chat').get(res.generated_keys[0]).run().then(function(data2){
io.sockets.in(data2.room_id).emit('chatNew',data);//emit to all users in room
log('chatSend');
//attempt to see if the other user is online
getOtherUser(data2.room_id,socket.client.user.id,function(tid){
log('other user id: %d',tid);
//all users automatically join a room matching their user id when connecting.
//unsure how to see if the user is online. this doesnt work.
//this is what i need help with. retrieving the other users socket resource if they are online,
//and if they are not then return null or false, etc so i can work with that.
var othersocket = io.sockets.in(tid);
//if timeout completes before they respond, they are not online.
var tmptime = setTimeout(function(){
log('other user not online.');
othersocket.removeListener('chatOnline',tmpfunc);
missedTexts(data2.room_id,tid,'INCR');
},5000);
var tmpfunc = function(){
clearTimeout(tmptime);
//remove the listener
othersocket.removeListener('chatOnline',tmpfunc);
};
//emit chatOnline to other user socket
//when they respond, cleartimeout, resulting in counter not being incremented.
othersocket.on('chatOnline',tmpfunc);
othersocket.emit('chatOnline');
});
});
});
}
});

how do I store socket resources from specific users with socket.io?

I'm designing a chat script which I test on my machine using different browsers. I'm tryng to send messages to specific users with socket.io, so here it is :
client:
socket.on('msgFromServer', function (data) {
message = data['message'],
from = data['from'],
to = data['to'];
if($('#chatbox.'+from).dialog("isOpen") === true){
$('#chatbox.'+from+' #messageOutput textarea.readOnly').text(message);
}
else if(($('#chatbox.'+from).dialog("isOpen") !== true)){
createChatbox(from,to,message);
}
});
server:
var users = {};
io.sockets.on('connection', function (socket) {
if( ( users.hasOwnProperty(req.session.name) === false))
users[req.session.name] = socket;
socket.on('msgToServer', function (data) {
for (var u in users){
console.log("%s | %s",u,users[u]);
}
});
});
Well, I'll talk about the structure of code related to the server. It is in charge of storing a user on a 'connection' event. The problem starts when I reload the page: it stores the user from browser A in the users object, if I reload and reconnect stores it again , but when I ask which are the contents of the users object in browser B ... the info is outdated and does not show the same result as when I ask which are the contents of the object in broser A, even though I'm trying to do some cheking of nullity to store vals if users is empty --> if( ( users.hasOwnProperty(req.session.name) === false)). Basically, what I need is a means of storing each socket resource in a container(in fact, doesn't necessarily needs to be an object) with an identifier(req.session.name) and to have such container available to all sessions in all browsers, so when server receives a message from browser A to browser B it could identify it and emit a response to browser B.
I got an I idea of what I wanted from https://github.com/generalhenry/specificUser/blob/master/app.js and http://chrissilich.com/blog/socket-io-0-7-sending-messages-to-individual-clients/
If you look carefully at the code... in chrissilich.com , the author states that we need to store the 'socket.id' (users[incoming.phonenumber] = socket.id), whereas in git generalhenry states we have to store the 'socket'(users[myName] = socket) resource. The latter is the correct one , because the values of socket.id tend to be the same in both browsers... and that value changes automatically , I don't know why is there... I suppose in earlier versions of node it worked that way.
The problem is that socket.id identifies sockets, not users, so if an user has several tabs opened at same time, every tab would have different socket.id, so if you store only one socket.id for an user, every time you assign it, you overwrite previous socketid.
So, beside other possible problems, at least you need to do this or it won't work. I bet that you say about 1 socket for all browsers is that you overwrite the id every time (it happened to me when I started using Socket.IO)
As a general rule, remember that you manage CONNECTIONS and not USERS... an user can have more than one connection!.
On connection
function onConnection( socket ) {
var arr = users[incoming.phonenumber] || null;
if( !arr )
users[incoming.phonenumber] = arr = [];
if( arr.indexOf( socket.id ) === -1 )
arr.push( socket.id ); // Assigns socket id to user
}
On disconnection
function onDisconnect( socket ) {
var arr = users[incoming.phonenumber] || null;
if( !arr ) return; // Should not happen since an user must connect before being disconnected
var index = arr.indexOf( socket.id );
if( index !== -1 )
arr.splice( index, 1 ); // Removes socket id from user
}

Resources