I'm thinking about making a room for every user that connects based on their user id. Will I run into memory problems doing things this way? Or will the rooms disappear when all the connected clients in that room are gone?
A room in socket.io is just a Javascript object with a list of sockets in it. A room only exists as long as there is a socket in it. As soon as the last socket in a room either disconnects or leaves the room, the room object itself is removed.
In case you didn't realize, socket.io already creates a room for each user based on the socket.id value. So, that already exists.
Will I run into memory problems doing things this way?
A room is not a large data structure and they are automatically cleaned up when there are no sockets in them so I would not expect you to have memory issues with rooms.
Or will the rooms disappear when all the connected clients in that
room are gone?
Yes, a room will be freed when all the connected clients in that
room are gone.
Rooms are stored in the adapter object (which allows an architecture where distributed rooms across multiple servers via redis can be supported) so the socket.leave(room) method ends up telling the adapter to remove the socket from a room. The adapter code looks like this:
Adapter.prototype.del = function(id, room, fn){
this.sids[id] = this.sids[id] || {};
delete this.sids[id][room];
if (this.rooms.hasOwnProperty(room)) {
this.rooms[room].del(id);
if (this.rooms[room].length === 0) delete this.rooms[room];
}
if (fn) process.nextTick(fn.bind(null, null));
};
You can see there that after the socket has been removed from the room, the code checks to see if the current length is 0 and, if so, it deletes the room object.
Related
i have some verticles that belong to the same cluster and they send a hello message over the event bus -the message is basically the sending verticle's name- to a receiver verticle which stores the message in a map.
i want to implement a keep alive method so if i kill one of the senders the message it sent to the receiver is deleted from the receiver's map.
i looked at the hazelcast website but i didn't find an easy way to implement that feature.
the expected result is to have the sender's record deleted from the receiver's map when the sender's verticle is stopped/killed.
You can register a MembershipListener and listen to the membership changes. This way you can remove the previously added messages of a member when it leaves the cluster.
There can be multiple ways to achieve this and one is using Hazelcast ILock's would be an easier approach, each member can take a lock (maybe with a verticle name), and when a member leaves the cluster, all the locks acquired by that dead member will be auto-removed so your application can check if it's locked or not anytime.
I was wondering if there was any way of attaching state to individual rooms, can that be done?
Say I create a room room_name:
socket.join("room_name")
How can I assign an object, array, variable(s) to that specific room? I want to do something like:
io.adapter.rooms["room_name"].max = maxPeople
Where I give the room room_name a state variable max, and max is assigned the value of the variable maxPeople that is say type int. Max stores a variable for the maximum amount of people allowed to join that specific room. Other rooms can/could be assigned different max values.
Well, there is an object (internal to socket.io) that represents a room. It is stored in the adapter. You can see the basic definition of the object here in the source. so, if you reached into the adapter and got the room object for a given name, you could add data to it and it would stay there as long as the room didn't get removed.
But, that's a bit dangerous for a couple reasons:
If there's only one connection in the room and that user disconnects and reconnects (like say because they navigate to a new page), the room object may get destroyed and then recreated and your data would be lost.
Direct access to the Room object and the list of rooms is not in the public socket.io interface (as far as I know). The adapter is a replaceable thing and when you're doing things like using the redis adapter with clustering it may work differently (in fact this probably does because the list of rooms is centralized into a redis database). The non-public interface is also subject to change in future versions of socket.io (and socket.io has been known to rearrange some internals from time to time).
So, if this were my code, I'd just create my own data structure to keep room specific info.
When you add someone to a room, you make sure your own room object exists and is initialized properly with the desired data. It would probably work well to use a Map object with room name as key and your own Room object as value. When you remove someone from a room, you can clean up your own data structure if the room is empty.
You could even make your own room object be the central API you use for joining or leaving a room and it would then maintain its own data structure and then also call socket.io to to the join or leave there. This would centralize the maintenance of your own data structure when anyone joins or leaves a room. This would also allow you to pre-create your room objects with their own properties before there are any users in them (if you needed/wanted to do that) which is something socket.io will not do.
I want to make multiple chatrooms like dynamic and I want to make them like this that if we refresh the page it retains the chat and users inside the rooms, I am new to socket.io and looking for the solution.
This is a pretty broad question so it's not really possible to be very specific with an answer. The general steps to retaining one or more "rooms" for a given user are as follows:
Install a session manager on your server. express-session is popular and you can then pick which data store you want to use with it (there are dozens of choices). This will give you a semi-persistent session object for each user who connects to your server.
When a user connects to your server for the first time, a session object and matching session cookie will be created that allows your server to identify that browser the next time it connects.
You can initialize a rooms property in that session to be an empty array or perhaps a Set object (which makes lookup a bit simpler for the later steps).
When this user gets put in a specific chat room, you add that room name to the session.rooms array or Set.
When the user leaves a specific chat room, you remove that room name from the session.rooms array or Set.
When a user re-connects, you look in their session and automatically join them to any rooms that the session says they should be connected to.
You probably need some sort of timeout process so that if a user remains disconnected for a certain period of time, you remove their session or at least clear their rooms list.
I'm learning how to use Socket.IO, and I'm building a small game.
When someone creates a room, I save the room values in a array.
var clients = [], rooms = [];
...
rooms.push(JSON.parse(roomData));
But if the server crashes, the server loses all the rooms Data.
Is it a good idea to save the data into a Database and repopulate the array with these values when the user connects to the server?
Thank you.
Restoring socket.io connection state after a server crash is a complicated topic that depends a lot on exactly what you're doing and what the state is. Sometimes the client can hold most of the state, sometimes it must be persisted on the server.
State can be stored to disk, to another in memory process like redis or in the client and presented when they reconnect.
You just have to devise a sequence of events on your server and then when the client reconnects for how everything gets restored. You will also likely need persistent client IDs so you know which client is which when they reconnect.
There are many different ways to do it. So, yes you could use a DB or you could do it a different way. There is no single "best" way because it depends upon your particular circumstances and tools you are already using.
We currently have a chat app whereby when emitting messages out to the appropriate users (could be 1 or several depending how many are in the conversation) we loop through all socket (Socket.io 2.0.2) connections to the server (NodeJS) to get a list of sockets that a user has based on a member ID value as each user could be connected from multiple devices. The code looks like this in order to determine which sockets a user has that we should be sending the message,
var sockets = Object.keys(socketList);
var results = [];
for (var key in sockets) {
if (hasOwnProperty(socketList[sockets[key]].handshake.query, 'token')) {
if (JSON.parse(socketList[sockets[key]].handshake.query.member).id === memberId) {
results.push(socketList[sockets[key]]);
}
}
}
Having to loop through the socket connections seems inefficient and I wonder is there a better way. My thought is to create a room for each user, most users will have only the one connection but some will be connected via multiple devices so they could have multiple sockets in their room. Then I would just broadcast to the appropriate rooms rather than always looping through all sockets. Given that 95% of users will only have the one socket connection I'm not sure if this approach is any more efficient or not and would appreciate some input on this.
Thanks.
First off, socket.io already creates a room for every single user. That room has the name of the socket.id. Rooms are very lightweight objects. They basically just consist of an object with all the ids of the sockets that are in the room. So, there should be no hesitancy to use rooms at all. If they fit the model of what you're doing, then use them.
As for looping yourself vs. emitting to a room, there's really no difference - use whichever makes your code simpler. When you emit to a room, all it does is loop through the sockets in the room and send to each one individually.
Having to loop through the socket connections seems inefficient and I wonder is there a better way.
The main advantage of rooms is that they are pre-built associations of sockets so you don't have to dynamically figure out which sockets you want to send to - there's already a list of sockets in the right room that you can send to. So, it would likely be a small bit more efficient to just send to all sockets in a room than to do what your code is doing because you code is dynamically trying to figure out which sockets to send to, rather than sending to an already made list. Would this make a difference? That depends upon how long the whole list of sockets is and how expensive the computation is to figure out which ones you want to send to. My guess is that it probably wouldn't make much difference either way.
Sending a message to a room is not much more efficient on the actual sending part. Each socket has to be sent the message individually so somebody (your code or the socket.io rooms code) is going to be looping through a list of sockets either way. The underlying OS does not contain a function to send a single message to multiple sockets. Each socket has to be sent to individually.
Then I would just broadcast to the appropriate rooms rather than always looping through all sockets.
Sending to a room is a programming convenience for you, but socket.io will just be looping under the covers anyway.
I would use Socket.io rooms to accomplish what you want to do.
Server side, adding a client to a chat room:
socket.join('some room');
Then I would use socket.to('some room').emit for a sender message to be sent to all participants in the room.