I am struggling to create an object for storing the number of clients in a room. It would look something like this: { Room0: 1, Room1: 4, Room2: 3}, and whenever a socket connection/disconnection occurs (joins or leaves a room), this object will be updated and all existing sockets can have access to it at all times.
Is there a simple way to do this?
You would set a variable in the global scope (outside of the onMessage or onEvent listeners). Then, in your join rooms code, you would have a part where you would add to the count of the room. It's a crude way to do it.
var rooms = {lobby: 0, kitchen: 0}
Inside your room joining section, lets say they joined the lobby, then you would do this inside that block:
rooms.lobby++
Then, any socket that wants to know the number of people in the lobby need only call:
rooms.lobby
Related
When I join the room, and then leave the route and go back, and then use the chat I've built, I get double messages of * amount of messages as many times I left and rejoined.
This problem goes away when I hard refresh.
I've tried everything I could find thus far, and have been unable to get it to work.
I tried on the client side, during beforeRouteLeave, beforeDestroy and window.onbeforeunload
this.$socket.removeListener("insertListener"); --> tried with all
this.$socket = null
this.$socket.connected = false
this.$socket.disconnected = true
this.$socket.removeAllListeners()
this.$socket.disconnect()
During the same events, I also sent a this.$socket.emit("leaveChat", roomId) and then on the server side tried the following inside the io.on("connection") receiver socket.on("leaveChat", function(roomId) {}):
socket.leave(roomId) --> this is what should according to docs work;
socket.disconnect()
socket.off() -- seems to be deprecated
socket.removeAllListeners(roomId)
There were a bunch of other things I tried that I can't remember but will update the post if I do.
Either it somehow disconnects and upon rejoining, previous listeners or something is still remaining, meaning all the messages are received * times rejoin. OR, if I disconnect, I don't seem to be able to reconnect.
On joining, I emit to server the room id and use socket.join(roomId).
All I want to do, is without refresh, when I leave the page, before that happens, the user can leave the room and when they go back, they get to rejoin, with no duplicate messages occurring.
I am currently trying to chew through the source code now.
Full disclosure here, I didn't read the full response posed by roberfoenix, but this is a common issue with socket.io and it comes down to calling the 'on' event multiple times.
When you create an .on event for your socket its a bind, and you can bind multiple times to the same event.
My assumption is, when a users hits a page you run something like
socket.on("joinRoom", data)
This in turn will say join the room, pull your messages from Mongo(or something else) and then emit to the room (side note, using .once on can help so you don't emit to every users when a user joings a room)
Now you leave the room, call socket.emit('leaveRoom',room), cool you left the room, then you go back into the room, guess what you now just binded to the same on event again, so when you emit, it emits two times to that user etc etc.
The way we addressed this is to place all our on-events into a function and call the function once. So, a user joins a page this will run the function like socketInit();
The socketInit function will have something like this
function socketInit(){
if (init === false){
//Cool it has not run, we will bind our on events
socket.on("event")
socket.on("otherEvent")
init = true;
}
}
Basically the init is a global variable, if is false, bind your events, otherwise don't rebind.
This can be improved to use a promis or could be done on connect but if a users reconnects it may run again.
If you're using Vue-Socket and feel like going slightly mad having tried everything, this may be your solution.
Turns out challenging core assumptions and investigating from the ground up pays off. It is possible that you forgot yourself so deeply in Socket.io, that you forgot you were using Vue-Socket.
The solution in my case was using Vue-Socket's built in unsubscribe function.
With Vue-Socket, one of the ways you can initially subscribe to events is as follows:
this.sockets.subscribe('EVENT_NAME', (data) => {
this.msg = data.message;
});
Because you're using Vue Socket, not the regular one, you also need to use Vue Socket's way for unsubscribing right before you leave the room (unless you were looking for a very custom solution). This is why I suspect many of the other things I tried didn't work and did next to nothing!
The way you do that is as follows:
this.sockets.unsubscribe('EVENT_NAME');
Do that for any events causing you trouble in the form of duplicates. The reason you'd be getting duplicates in the first place, especially upon rejoining post leaving a room, is because the previous event listeners were still running, and now the singular user would be playing the role of as if two or more listeners.
An alternative possibility is that you're emitting the message to everyone, including the original sender, when you should most likely be emitting it to everyone else except the sender (check here for socket.io emit cheatsheet).
If the above doesn't solve it for you, then make sure you're actually leaving the room, and doing so server-side. You can accomplish that through emitting a signal to the server right before leaving the route (in case you're using a reactive single page application), receiving it server side, and calling 'socket.leave(yourRoomName)' inside your io.on("connection", function(socket) {}) instance.
I'm looking for a method or a command line to get the socket room name if possible. Any ideas or tips are highly appreciated! For example if the name of the room is 'roomName', I'm looking to get this value, ty!
A socket.io socket can be in multiple rooms. In fact, it is automatically placed into a room with the same name as the socket.id value when the socket first connects.
If what you're trying to do is get a list of all the rooms a socket is in on the server-side of the connection, you can use socket.rooms. That is an object that lists all the rooms the socket is in. If you want just an array of room names the socket is in, you can use this:
let rooms = Object.keys(socket.rooms);
If you want to eliminate the auto-generated room that matches the socket.id, you can do this:
let rooms = Object.keys(socket.rooms).filter(function(item) {
return item !== socket.id;
});
If you're trying to get this information from the client-end of things, it is not available from the client-side socket. You would have to ask the server which rooms this client is in or you'd have to have a system where the client was kept notified of what rooms it was being put in and it kept track of that data. The socket.io client does not know what rooms it is in.
One other thing to be careful of in socket.io. I've seen some asynchronous behavior in joining rooms which means that if you socket.join("someRoom") and then immediately query socket.rooms, the new room might not be there yet. If you query socket.rooms on the next tick, it will be there. I'm not sure if this always happens or just happens in some circumstances.
Hi I am using websockets with node js server, the npm module is ws. I have an array where I save all my connections but now I have to separate them so I did multidimensional array something like this:
users[channel1][user_id1] = ws_user_id1_connection
The question is when I have 1 user in multiple channels:
users[channel1][user_id1] = ws_user_id1_connection
users[channel2][user_id1] = ws_user_id1_connection
users[channel3][user_id1] = ws_user_id1_connection
From preformance point of view, is this ok?. or I can accomplish this in some other way? And if I leave it like this, Is that users[channel1],users[channel2],users[channel3], they would be only reference to the ws_user_id1_connection. I mean It's not going to add the all data about ws_user_id1_connection when I create new users[channelNew], but only reference to it. The Idea is that I would like to have something as rooms/channels and in each channel to have some connected users so they can talk each other. Is that the right way? Thank you in advance.
Assuming that channel1 is a chat room, user_id1 is the userid in the room, then yes, that's a good way to implement it, you should not create a different ws per channel. You will just need to add some information to the sent data so the client knows what is the room related to the message, something like:
{
'room': 'channel1',
'from': 'otherUser_id',
'msg': 'some text message'
}
I would recommend not to use channel to refer a room because it can be confused with a ws channel. I´d also change the name of the variable ´users´ as it is not referencing users, I´d leave it like: rooms[room_id1][user_id1] = ws_user_id1_connection
Also, you may want to check Socket.io, it is a good Nodejs library designed for that kind of applications.
I need to know how to omit in sails.js two or more sockets in sails.sockets.broadcast? I tried this:
function sendMessage(data){
var socketIds = ['socketId1','socketId2'];
sails.sockets.broadcast("room","event",data,socketIds);
//sending data to ALL sockets in the room :/
}
but it doesn't work.
I need know this because I need omit the sockets which belong to the same session. (example: session of user in computer browser and android browser)
somebody help?
There's nothing built-in that will do this for you, but broadcast is just a wrapper around emit anyway, so you can just roll your own by getting all of the socket IDs in the room you want to broadcast to, and omitting the IDs in your array.
// Get all the IDs of the sockets subscribed to "room"
var socketIds = sails.sockets.subscribers("room");
// Remove the IDs you want to omit
socketIds = _.difference(socketIds, ['socketId1','socketId2']);
// Emit your event to the rest!
sails.sockets.emit(socketIds, "event", data);
Well, title says it all. How can I get the rooms with most joined sockets? A way to list them ordered by sockets would be neat. Also if there is a way, how efficient is it? And if it isn't, can one make an own efficient way?
Not really tested, but I think this might work:
var roomNames = Object.keys(io.sockets.manager.rooms);
var sortedByNumberOfClients = roomNames.sort(function(room1, room2) {
return io.sockets.manager.rooms[room2].length - io.sockets.manager.rooms[room1].length;
});
// sortedByNumberOfClients is an array of room names, sorted on
// number of clients in the room, largest room first.
I do believe that there's a default room with an empty string ('') as name that contains all clients, so you may want to skip that. Also, I think internally socket.io adds a leading / to each room name.