socket.io list of all connected - node.js

I'v create two side project. side-1 is admin side, side-2 is clients side. What I want is that, admin, can list all connected clients and see their connected status(online-ofline).Im use node.js+express.js+socket.io. help please. thanks.

You can get connected clients using client key and status that you are using in your socket.
Example:
Suppose to fetch online clients, you have payload with key name clients, like clients:
{
{
id: 'client 1',
status:'offline'
},
{
id:'client 2',
status:'online'
}
}
Now you can get online clients like below,
const onlineClients = Object.values(io.of(namespace).connected)
.map(socket => socket.clients)
.filter(client => client && client.status === 'online');

Related

Is there a way to append addtiional clients manually to a socket room created by a connected client?

I'm having difficulties in finding a solution for a scenario when users are mentioned in the comments section. They receive a notification regardless of their online presence.
module.exports = function (io) {
io.on('connect', function (socket) {
// pre-processing operations to get the users
let users = ['user1', 'user2', 'user3'];
socket.join('room', () => {
// how would I manually join `users` to this room
}
// While broadcasting a message only to those users who were manually joined
socket.to('room').emit('event', {"message" : "You were mentioned in this conversation"})
}
Any suggestions, resources to look into?
There are scenarios where I want users who are part of the conversation and not using the application.

how to create a room based on 2 users in socket.io?

My goal is to create a one to one chat based on two different users. The only way that I could think of is to use socket.io rooms
But the problem right now is that how do i create unique room?
For example
socket.on('join', function(room) {
socket.join(room);
});
Do i need to emit the room from the client, if so , how do I make it unique. Imagine there are thousands of users.
The chat application, is similar like facebook chat application. Where you can chat one on one.
Do i need redis or mongodb to store the room? Anyone of you who have experience using socket.io in scale, please do share your opinion
Thanks!
A room always will be unique, when you do socket.join('roomname') if the room not exist it will created and this socket will join it, if exist the socket just will join it.
So if you want that client A join in the room where is client B for example, from client A you can send a event like:
socket.emit('joinroom', roomname);
On sever:
socket.on('joinroom', function(data){
socket.join(data)
})
Anyway when a socket connect , it create and join his own room automatically , the name of this room will be the id of the socket, so i think is not neccessary create new rooms for a chat based on two different users.
Everything that you need is link the socket id with a permanent property of the user.
EDIT:
I leave you here a simple chat app example where you can open multiple conversations:
server.js: https://gist.github.com/pacmanmulet/b30d26b9e932316f54b2
index.html: https://gist.github.com/pacmanmulet/6481791089effb79f25f
You can test it here :https://chat-socket-io-example.herokuapp.com/
I did not use rooms, it have more sense when you want emit to a group of sockets, not to a only one.
Hope you can understand better my idea with that.
you need to store the room number somewhere(any database).You have to do this because you have to keep your server stateless.
Let us assume that you are creating a private chat only for two people.The room number has to be unique. so one approach is to use the user's email id and join them to create a new string and emit it back to the users.this is tricky because we don't know the order in which the strings are joined. so we join them by a string not used in normal email name(eg :'"#!#!#!!#!#!#!').we can split it on the server side and compare emit the results.
The actual message body will be
{
room:a#gmail.comb#gmail.com,
from:a,
message:'hi buddy how are you?'
}
CLIENT side code
const roomName = a#gmail.com+'#!#!2!#!#"+b#gmail.com
socket.emit('room', { room: roomName });
this.socket.on('joined', data => {
console.log('i have joined', data.room)
store the room name room: data.room })
})
socket.on('chat',data=>console.log(`received chat from ${data.from} from the message room ${data.room}`)
used '#!#!2#!#' just because we can separate them on the server side and check if the room already exists.
SERVER side code
const room =[]//this variable you have store in database and retrieve it when needed.
socket.on('room',data=>{
if(room.length!=0){
const temp = data.room.split('!#!#2#!#!').reverse().join('!#!#2#!#!');
if(room.includes(temp)){
socket.join(temp)
console.log('joined room',temp)
socket.emit('joined',{room:temp})
console.log(room);
} else if(room.includes(data.room)){
socket.join(data.room)
console.log('joined room', data.room)
socket.emit('joined', { room: data.room})
console.log(room);
}
}else{
socket.join(data.room);
room.push(data.room)
console.log('joined room',data.room);
socket.emit('joined', { room: data.room })
console.log(room);
}
})
I tried to do a minimal example of where you can only be in one room at a time (apart from your default socket.id room) and only other sockets in the same room as you will receive your messages. Also you can change rooms.
The basic premise is, if socket A is in room 'xyz' and so is socket B, on the server side you can do socket.to('xyz').emit('message', 'hello') for socket A, and socket B will receive the message, but another connected socket C which isn't in room 'xyz' won't.
You can create room at server runtime, I used both users id as room id, Ex : '100-200' for demo purpose. May be you can use some more complex approach.

Why use redis in a chat application? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I just recently built a chat, it's working pretty well, but I think I need to hook it up to redis.
From what I understand I need redis for scaling and holding some data if a client refreshes or a server goes down.
A core component of the 1on1 chat is that I store the users, and associate a socket.id to those users
var users = {};
io.sockets.on('connection', function (socket) {
// store the users & socket.id into objects
users[socket.handshake.headers.user.username] = socket.id;
});
Now on the client side I can say hey I want to chat with "Jack", as long as that is a valid user then I can pass that data to the server, i.e the user name and message just to jack like so.
var chattingWith = data.nickname; // this is Jack passed from the client side
io.to(users[chattingWith]).emit();
My question is, why should I use redis? What should I store in redis? How should I interact with that data?
I am using an io.adapter
io.adapter(redisIo({
host: 'localhost',
port: 6379,
pubClient: pub,
subClient: sub
}));
Also reading code from an example app I see when a socket connects they save the socket data into redis like so.
// store stuff in redis
redisClientPublish.sadd('sockets:for:' + userKey + ':at:' + room_id, socket.id, function(err, socketAdded) {
if(socketAdded) {
redisClientPublish.sadd('socketio:sockets', socket.id);
redisClientPublish.sadd('rooms:' + room_id + ':online', userKey, function(err, userAdded) {
if(userAdded) {
redisClientPublish.hincrby('rooms:' + room_id + ':info', 'online', 1);
redisClientPublish.get('users:' + userKey + ':status', function(err, status) {
io.sockets.in(room_id).emit('new user', {
nickname: nickname,
provider: provider,
status: status || 'available'
});
});
}
});
}
});
They use it when entering a room, to get information about the room.
app.get('/:id', utils.restrict, function(req, res) {
console.log(redisClientPublish);
utils.getRoomInfo(req, res, redisClientPublish, function(room) {
console.log('Room Info: ' + room);
utils.getUsersInRoom(req, res, redisClientPublish, room, function(users) {
utils.getPublicRoomsInfo(redisClientPublish, function(rooms) {
utils.getUserStatus(req.user, redisClientPublish, function(status) {
utils.enterRoom(req, res, room, users, rooms, status);
});
});
});
});
});
So again, I am asking because I am kind of confused if I need to store anything inside redis/why I need to, for instance we may have a few hundred thousand users and the node.js server "Jack" and "Mike" are chatting on goes down, it then changes to point to a new node.js instance.
Obviously I want the chat to still remember "Jack's" socket id is "12333" and "Mike's" socket id is "09278" so whenever "Jack" says hey I want to send "Mike/09278" a message the server side socket will direct it properly.
Would storing the username as a key and socket ID as a value be a wise use case for redis, would that socket.id still work?
Redis is a pretty good choice as a database for a chat as it provides a couple of data structures that are not only very handy for various chat use cases but also processed in a really performant way. It also comes along with a PubSub messaging functionality that allows you to scale your backend by spawning multiple server instances.
Scaling socket.io with the socket.io-redis adapter
When you want to run multiple instances of your server - be it because of one server not being able to handle increasing users any more or for setting up a high availablility cluster - then your server instances must communicate with each other in order to be able to deliver messages between users who are connected to different servers. The socket.io-redis adapter solves this by using the redis PubSub feature as a middleware. This won't help you if you are using only a single server instance (in fact I assume it will be slightly less performant) but as soon as you spawn a second server this will work out just fine without any headaches.
Want to get a feeling and some insight on how it's working? Monitor your dev redis while using it and you'll see the internal socket.io messages that are pushed through redis.
redis-cli
monitor
Use cases and their according redis data types
Save active conversations in a SET
A redis set is a collection of unique strings. I don't think storing socket.io id's would work out well as you can't assume that a user will get the same id on a reconnect. Better store his rooms and rejoin him on connect. You add every chat room (btw. direct messages can be defined as a room with two participiants so the handling is the same in both cases) that a user enters to their room set. On a server restart, a client reconnect or second client instance you can retrieve the whole set and rejoin users to their rooms.
/* note: untested pseudo code just for illustration */
io.sockets.on('connection', function (socket) {
rooms = await redis.smembers("rooms:userA");
rooms.foreach (function(room) {
socket.join(room);
}
socket.on('leave', room) {
socket.leave(room);
redis.srem("rooms:userA", room);
}
socket.on('join', room) {
socket.join(room);
redis.sadd("rooms:userA", room);
}
}
Save the last 10 messages of a conversation using a redis LIST
A redis list is somewhat of an persistent array of strings. You push a new message into a list and pop the oldest when the list size reaches your threshold. Conveniently the push command returns the size right away.
socket.on('chatmessage', room, message) {
if (redis.lpush("conversation:userA:userB", "Hello World") > 10) {
redis.rpop("conversation:userA:userB");
}
io.to(room).emit(message);
}
To get the message history use lrange:
msgHistory = redis.lrange("conversation:userA:userB", 0, 10)
Save some basic user details in a HASH
A hash is a key/value collection. Use it to store the online status along with avatar urls or whatever.
io.sockets.on('connection', function (socket) {
redis.hset("userdata:userA", "status", "online");
socket.on('disconnect', function () {
redis.hset("userdata:userA", "status", "offline");
}
}
Maintain a "recent conversations" list in a SORTED LIST
Sorted sets are similar to SETs but you can assign a score value to every element and retrieve the set ordered by this value. Simply use a timestamp as score whenever there is an interaction between two users and that's it.
socket.on('chatmessage', room, message) {
io.to(room).emit(message);
redis.zadd("conversations:userA", new Date().getTime(), room);
}
async function getTheTenLatestConversations() {
return await redis.zrange("conversations:userA", 0, 10);
}
References
socket.io-redis: https://github.com/socketio/socket.io-redis
redis PubSub docs: https://redis.io/topics/pubsub
redis data types: https://redis.io/topics/data-types-intro

How to handle user and socket pairs with node.js + redis

Straight to the point:
I am using node.js, socket.io and redis for a private chat system.
On connect user passes his website id (userID) to node.js server. He may have multiple connections so I have to pair socketID (of each connection) and userID somehow. I has thinking about using redis to store userID->sessionID pairs. However, when user disconnects I need to remove that pair from redis.. but I have only socketID not userID so I can't select by that key..
Now, am I approaching this the wrong way or should I store both userID->socketID and socketID->userID pairs? Maybe someone could offer more elegant solution?
A more elegant solution would be to make each socket connect to the channel userID, for example:
io.sockets.on('connection', function (socket) {
socket.join(userID);
});
// when you want somebody to send a message to userID you can do:
io.sockets.in(userID).emit(message);
There are two things you need to take care of here:
Make sure that only userID can connect to his channel, thus verify the session ( read more here: http://www.danielbaulig.de/socket-ioexpress/ )
On connection increase the value for userID in redis (so that you know a new connection for that user is listening) and on disconnect decrease the userID value (so that you know the number of connections still listening). If the value is 0 then you emit a message to the chat stating that userID has left (since the number of connections listening to the userID channel is 0).
When other users will want to send a message to userID, they don't need to connect to the userID channel, they can send a message to the chat channel and pass userID as a property. For example:
var chat = io
.of('/chat')
.on('connection', function (socket) {
// connected to public chat
})
.on('message', function (data) {
if (data.userID && data.message) {
io.sockets.in(userID).emit('UserX: ' + data.message);
}
});

Connecting to Conference over node-xmpp on Node.js

How to connect to a Jabber conference? Send and receive messages. Get a list of online users.
Did some testing from localhost using prosody.im. I had two clients:
alfred1: Just normally XMPP client
alfred2: My bot
I created MUC alfred#conference.localhost.
When I first connect to channel from XMPP client(alfred1) and next run the bot, I receive test message from bot(alfred2). And I will also receive chat message from alfred1 in console when I sent message from XMPP-client.
var xmpp = require('node-xmpp'),
sys = require('sys'),
jid = 'alfred2#localhost',
password = '1234',
room_jid = 'alfred#conference.localhost',
room_nick = 'alfred2',
conn = new xmpp.Client({
jid : jid,
password : password,
});
conn.on('online', function () {
console.log('online');
//var elm2 = new xmpp.Element('presence', { from: jid, to: 'alfred#conference.localhost'}).c('x', {'xmlns': 'http://jabber.org/protocol/muc' }).up();
conn.send(new xmpp.Element('presence', { to: room_jid +'/' + room_nick }).
c('x', { xmlns: 'http://jabber.org/protocol/muc' })
);
conn.send(new xmpp.Element('message', { to: room_jid, type: 'groupchat' }).
c('body').t('test')
);
});
conn.on('stanza', function(stanza) {
sys.puts(stanza);
});
conn.on('error', function(e) {
sys.puts(e);
});
Maybe later I try to improve code a bit, but I guess this gets you going...
From jabber.org:
but as always feel free to join the
jabber#conference.jabber.org chatroom
via XMPP or HTTP if you have questions
or comments.
You mean connecting to jabber#conference.jabber.org?
I believe that should look up MUC specifications for that. I think it is possible using only node-xmpp, but node-xmpp is pretty low-level library. I used npmjs.org's search to look for modules supporting MUC, but could not get any of them working yet.. I think MetaJack's source-code about MUC could help you out. This could be a fun project to implement over the weekend I guess.
When you like to get started immediately you should probably(maybe somebody has MUC in node-xmpp?) have a look at Smack(Java) for example which does support MUC.

Resources