Two instances of one socket.io connection - node.js

In our app, every time a user signs in a new connection in socket.io is created. So if a user signs in simultaneously on more devices, they behave as separate connections. Instead of creating a new connection every time, I'd like to check whether the user is already connected to socket.io and if he is, I'd like to connect him to already established connection. How can this be done?

From the socket.io perspective, those ARE two connections. But if it was my app, I would do something like this:
add a user identifier (userid, username, something you identify users by) to both the users' socket.io connection (so, each time you send a message to the client, you also pass this id)
pass this id also to the client-side code on init.
So now the socket on the server side has its' socket.io id, but also your user id. Anyway, to proceed:
send this user-id to the client in it's javascript files.
on the client-side code, make a small adjustment to socket.io handler - for each received message (say, broadcast), you can now check if it's your current user-id and instead of saying 'User #351 says Hi' you can say 'You said Hi on another device' or something like that.
Of course, I'm not socket.io expert, there's could already exist a framework or lib addressing this.

Related

how to access socket session in all clusters

I am working on setting up socket.io in cluster mode using PM2.
I am using socket.io-redis package and it works fine in cluster mode.
But the problem arises when I want to access all connected sockets. Because processes don't know about socket connections in other processes in cluster mode.
I thought socket.io-redis keeps track of all the connected sockets and all its session info but it didn't.
Is there any way or solution to access all the socket connection existing in all processes in socket.io/Nodejs?
Socket.io-redis does keep track in a sense..
From their docs
"The Redis adapter extends the broadcast function of the in-memory adapter: the packet is also published to a Redis channel (see below for the format of the channel name).
Each Socket.IO server receives this packet and broadcasts it to its own list of connected sockets."
So basically, redis is used as the broker to tell each socket server to emit based on X channel etc. Allowing you to have a socket.io server in cluster mode work, but as you have mentioned it can fall short when you need to keep track of things outside of just an emit.
So where does this leave us.. Well you can use custom hooks via socket.io-redis but personally I found it to be really difficult to understand and use and had limited success personally. I think with the new version of socket.io and socket.io redis there were some tweaks to make this simpler however I have not tried them.
Instead, what we do is use redis hset and jget to store the socket and an ID of a users, then when we want to get all users online we can query redis to get the list of online users or users in a specific room etc.
What you will want to do is add the redis package and connect in additon to the regular pub / sub.
Then, when a user joins a room or your server for that matter you will do an hset. On the first join ours looks something like this
redis.hset([collection-name],[Field],[value])
So in code it looks like
redis.hset(decoded.cID,"socket-" + socket.id,socket.nickname)
This will set a value in redis, so the collection name is a value ( for us its a unique id of the channel ) then we stock the 'socket.id' for the Field along with a 'nick-name' for the value. This value is the users ID OR its anonymous if they are not logged in
Then, when we want to grab who is in a room we use the hget command
redis.HGETALL([collection-name],function(err,results){}
So inside of say the emit, we call the redis.HGETALL command to get all items inside a specific collection that we pass in and send that back to all connected users.

Using socket.io-redis & multiple servers/processes. How do I send a message to a specific socket ID with a callback?

I have multiple processes & servers, and am using socket.io-redis with session affinity (on Heroku).
When a client connects, I store a map of the client's user ID to the client's socket ID in Redis. Upon disconnecting, I delete the client's user ID from Redis.
I would like to be able to push a message to a client by looking up the client's socket ID, then sending a message to the specific socket ID.
One way to do this is io.to(socketId).emit(...). However, this does not allow me to send a callback with the message, which I would really like because I want to have confirmation of message receipt.
Another way I have found is io.connected[socketId].emit(...). The problem with this is that it does not scale to multiple servers, since the io.connected object may not necessarily be the same on every server since it only records sockets that the server itself is connected to. This solution does allow callbacks, however.
Is there a way to solve this so I can emit messages to a specific socket ID from any server or process, and also send a callback with the message?

Socket.io - Emit broadcast message except my sockets id

I'm building an application in real time with socket.io
Each time the user enters the application creates a socket ID.
So if I joining the application in several browser tabs generates me a socket id for each tab.
I generate a room for specific users and included my sockets id,
but I need to emit broadcast message except to my sockets id.
This code don't work for me because emit broadcast message but sends to my other id sockets
socket.broadcast.to("room").emit('message', "somethings")
Any ideas?
Assuming you are using some application framework like express along side socket.io, you can use the valid session id obtained from Express.js for maping the socketIDs generated from the same session.
Other than maping the socketIDs to the sessionID, I dont think there is a way to obtain all the socketIDs generated across tabs as the information on other tabs wont be accessible in the current tab context.
Below are few existing questions discussing this same case:
manage-multiple-tabs-but-same-user-in-socket-io
socket-io-and-session

How to send real time notification to single user using node.js and PHP

I am trying to integrate real time notifications with Node and socket.io in a Symfony Application. I have read a lot of information about this topic and have a working Node application.
nodeClient.js
var socket = io.connect( 'http://192.168.15.106:8080' );
$('a.sendSmile').click(function(){
socket.emit( 'message', { name: 'something' } );
});
socket.on('message', function(data){
console.log(data.name);
});
The problem now is with the above which is working perfectly I am able to send real time notification to all the users at once. But what's the best way to target a single user?
For example a user can send a smile to another user so only the second user should receive the notification and not all the users.
Should I make multiple listeners for node? or any other method to do this?
You need some way of identifying which socket that connected to your server is the one you want to send data to and then you can send to just that socket. You can keep track of user names when users connect or if you have some auth system, you can keep track of which socket belongs to which authenticated user.
Your server holds a list of connected sockets. Each connected one at a time and triggered a connection event on your server when they connected. Your application needs to create a way of knowing which of those connected sockets you want to send the data to. This is not something you've described anything about how you want that to work so we can't really help more specifically.
You can dispatch a notification to single user if you can discriminate that user. For example you can get a user_id on client connection to your nodejs server (the user_id is send from client, inside message) and save it in a key-value store (like Redis, memcache, ...). In this way you can correctly dispatch the notification, arrived from the server (like Symfony2 application), to right user.
I suggest you use Redis, both as a key-value store and for its implementation pattern of the publish/subscribe usable as a channel of communication between the server and the application of realtime.

How do I restrict access to a broadcast message in NodeJS?

I understand routing (I think), but what's to stop User A from looking at User B's client-side source to discover the socket.io URL specific to each user and then build their own script to start listening for broadcast messages on that particular channel?
You would need to use something to determine who the client is. Once you do that, you could for example use the rooms feature, and only broadcast to the specific room where you put all your authorized users.
To identify the client, you could pass in cookies from the browser in a socket.io request, and compare that to any session data you have stored in Express/Connect/Whatever.

Resources