-
Notifications
You must be signed in to change notification settings - Fork 487
io.of('/').connected[socketId] does not always return socket object #283
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Anyone on this issue? |
Anyone on this? |
The socket is not shared accross all nodes: // node1
io.on('connect', (socket) => {
// io.sockets.connected[socket.id] === socket
});
// node2
// io.sockets.connected[socket.id] == undefined |
@darrachequesne any suggestion to resolve this problem? |
@joanmooreban @jaymehtasa therefore, if you really want some "attributes" of socket object you can use... solutions: customHook, customRequest, You can use customRequest to communicate with other node instance and retrieve some attributes of "socket object" by specific socketId. hook the function on your application, and use customRequest to get you wanted. |
can anyone post working example for this issue ? |
@Manjukb you need to use Redis for sharing Socket instance across node cluster. You may use package for NodeJS - https://github.com/NodeRedis/node-redis |
@jaymehtasa thanks for your replay |
NodeJS const socketIoRedis = require('socket.io-redis');
const redis = require('redis');
const redisClient = redis.createClient(<Redis_Port>, <Redis_Host>, {
no_ready_check: true,
});
if (process.env.REDIS_URL && process.env.REDIS_PORT) {
IO.adapter(socketIoRedis({ host: <Redis_Host>, port: <Redis_Port>, key: 'socket.io' }));
logger.info('Connecting Redis');
} else {
logger.warn('REDIS_URL and REDIS_PORT are not set, socket.io messages will not be shared across servers.');
}
const onConnection = (socket) => {
const userId = socket.user.id; // Some unique id of the system...
// Save Socket ID of User to Redis
redisClient.hset("REDIS_LIVE_SOCKET_USERS", userId, socket.id);
socket.on('listenToEventSomeGoodEvent', (data) => {
console.log('listenToEventSomeGoodEvent Fired...');
// Read Socket ID of User from Redis
redisClient.hget("REDIS_LIVE_SOCKET_USERS", userId, (err, socketId) => {
if (socketId) {
io.to(`${socketId}`).emit('sendDataUserEvent', data);
}
});
});
};
const nspChat = io.of('/');
socketIo = nspChat;
.on('connection', onConnection); |
ok, i use
when i pass thanks a lot for your help |
@Manjukb I will not able to help further as I have never worked with FYI, the code I have provided works 100% because it's in production and being used by 3M people, we have tons of NodeJS servers running in parallel in cluster mode. We have never faced any issue, you may change your architecture the way I have suggested, it must work. |
@jaymehtasa I don't understand your example, couldn't you just use a plain emit in that case? redisClient.hget("REDIS_LIVE_SOCKET_USERS", userId, (err, socketId) => {
if (socketId) {
// io.to(`${socketId}`).emit('sendDataUserEvent', data);
socket.emit('sendDataUserEvent', data);
}
}); Did you mean storing something like the @Manjukb currently, I'm afraid you won't be able to use That's a known limitation in the current implementation of acknowledgements. To share information between servers (though I'm not sure that's the use case here), customHook/customRequest is the way to go (credits to HsinHeng's anwser): // on every node
io.of('/').adapter.customHook = (socketId, cb) => {
const socket = io.sockets.connected[socketId];
cb(socket ? socket.handshake : null);
}
// then
const socketId = 'abcd';
io.of('/').adapter.customRequest(socketId, function(err, replies){
console.log(replies); // an array [null, <handshake>, null, ...] with one element per node
}); |
My example is for sending an event to Recipient User, it might be different than the sender, and can be on any node cluster, so we need to read from the central Redis server, Hope that makes sense. socket.on('listenToEventSomeGoodEvent', (data) => {
console.log('listenToEventSomeGoodEvent Fired...');
// Read Socket ID of Recipient User from Redis
redisClient.hget("REDIS_LIVE_SOCKET_USERS", <recipient_user>, (err, socketId) => {
if (socketId) {
io.to(`${socketId}`).emit('sendDataUserEvent', data);
}
}); |
@jaymehtasa that does make sense! I think you should also be able to use a room for that: const onConnection = (socket) => {
const userId = socket.user.id; // Some unique id of the system...
socket.join(userId);
socket.on('listenToEventSomeGoodEvent', (data) => {
console.log('listenToEventSomeGoodEvent Fired...');
io.to(<recipient_user>).emit('sendDataUserEvent', data);
});
}; |
@darrachequesne yes i ditched |
@darrachequesne sorry being straight, but I still think the approach you suggested may not work in case of node cluster / horizontally scalable architecture, you need something to centralize socket connections which can Redis or similar service. the room approach is good when you want to broadcast something for more than one person, in case of one to one communication, I still prefer to go with sending a direct message to the user... even if we use rooms, we need Redis to centralize otherwise, one user might have joined on server-1 and another user might have joined on server-2, they cannot share the room unless it is shared across servers/clusters. |
This commit adds the following methods: - fetchSockets: returns the matching socket instances Syntax: ```js // return all Socket instances const sockets = await io.fetchSockets(); // return all Socket instances of the "admin" namespace in the "room1" room const sockets = await io.of("/admin").in("room1").fetchSockets(); ``` - socketsJoin: makes the matching socket instances join the specified rooms Syntax: ```js // make all Socket instances join the "room1" room io.socketsJoin("room1"); // make all Socket instances of the "admin" namespace in the "room1" room join the "room2" room io.of("/admin").in("room1").socketsJoin("room2"); ``` - socketsLeave: makes the matching socket instances leave the specified rooms Syntax: ```js // make all Socket instances leave the "room1" room io.socketsLeave("room1"); // make all Socket instances of the "admin" namespace in the "room1" room leave the "room2" room io.of("/admin").in("room1").socketsLeave("room2"); ``` - disconnectSockets: makes the matching socket instances disconnect Syntax: ```js // make all Socket instances disconnect io.disconnectSockets(); // make all Socket instances of the "admin" namespace in the "room1" room disconnect io.of("/admin").in("room1").disconnectSockets(); ``` Those methods share the same semantics as broadcasting. They will also work with multiple Socket.IO servers when using the Redis adapter. In that case, the fetchSockets() method will return a list of RemoteSocket instances, which expose a subset of the methods and attributes of the Socket class (the "request" attribute cannot be mocked, for example). Related: - #3042 - #3418 - #3570 - socketio/socket.io-redis-adapter#283
This commit adds the following methods: - fetchSockets: returns the matching socket instances Syntax: ```js // return all Socket instances const sockets = await io.fetchSockets(); // return all Socket instances of the "admin" namespace in the "room1" room const sockets = await io.of("/admin").in("room1").fetchSockets(); ``` - socketsJoin: makes the matching socket instances join the specified rooms Syntax: ```js // make all Socket instances join the "room1" room io.socketsJoin("room1"); // make all Socket instances of the "admin" namespace in the "room1" room join the "room2" room io.of("/admin").in("room1").socketsJoin("room2"); ``` - socketsLeave: makes the matching socket instances leave the specified rooms Syntax: ```js // make all Socket instances leave the "room1" room io.socketsLeave("room1"); // make all Socket instances of the "admin" namespace in the "room1" room leave the "room2" room io.of("/admin").in("room1").socketsLeave("room2"); ``` - disconnectSockets: makes the matching socket instances disconnect Syntax: ```js // make all Socket instances disconnect io.disconnectSockets(); // make all Socket instances of the "admin" namespace in the "room1" room disconnect io.of("/admin").in("room1").disconnectSockets(); ``` Those methods share the same semantics as broadcasting. They will also work with multiple Socket.IO servers when using the Redis adapter. In that case, the fetchSockets() method will return a list of RemoteSocket instances, which expose a subset of the methods and attributes of the Socket class (the "request" attribute cannot be mocked, for example). Related: - socketio#3042 - socketio#3418 - socketio#3570 - socketio/socket.io-redis-adapter#283
Uh oh!
There was an error while loading. Please reload this page.
Before implementing socket.io-redis I used the following to get the socket object:
var socket = io.sockets.connected[socketId];
Now that I've implemented socket.io-redis I guess the command to replace it is:
var socket = io.of('/).connected[socketId];
However, it returns undefined in many, many cases where the socket actually exists.
I need to know how to get a socket's object by its id but without using any callback function since in my initial implementation I used as a return value as showed above and it is being currently used in many parts of the app.
UPDATE Jun 20th
I've also unsucessfully tried with
io.of('/').adapter.nsp.connected[socket_id];
. It replies with the right socket object for just some cores, not for all of them, returning undefined most of the time.The text was updated successfully, but these errors were encountered: