I am relatively new to node.js and have been looking it up for quite some time now. I have a problem regarding the handling of multiple chat rooms.
When a user connects, they automatically join the room that I have setup. However, the problems is that the connection is persistent and I wish to address the problem by using the socket.leave(room) method.
My question is, how can this be handled on the client side?
Am I right in putting this on my script?
server.js
io.sockets.on( 'connection', function( socket ) {
socket.on('join', function(room) {
socket.join(room);
console.log("User joined "+room);
});
socket.on('disconnect', function(room) {
socket.leave(room);
console.log("User left "+room);
});
On the client, how can I trigger the disconnect event? Should I have it called on Page Unload?
EDIT: Additional Info
User 1 connects to room 1
User 2 connects to room 1
This is a good and great room connection.
However, once there's a switch of rooms
User 1 connects to room 2
User 3 connects to room 2
User 1 is not able to receive the messages from User 3 because he is still listening to room 1
This is the reason why I wish to manage the rooms that the user wishes to join to, hence my question on how to force a User to leave a specific room.
Any advice would be greatly appreciated.
If you emit some event like change room from your client code when user changes room like this:
socket.emit("change room", {newroom: "room name"});
Let's suppose a client has already joined a room as:
socket.on('join', function(room){
socket.room = room;
socket.join(room);
});
Then you can do this in server to change the room of that client:
socket.on("change room",function(data){
socket.leave(socket.room);
socket.join(data.newroom);
});
You don't have to call socket.leave() in disconnect event. You can do it in your own logic. Hope you can understand this!
Related
suppose I have an room_id and I am registering my users to it.
let room_id = "2133"
socket.join(room_id, function() {
socket.emit(room_id, { message: `application joined: ${room_id}` })
});
and this works fine. but listening to this room is open for everyone.
if user=admin1 registers to room_id="2133", the user="james" can listen to the room_id="2133" and get all the traffics.
how to add authorize lvl to room?
I confused about the definition of room and namespace.
joining the clients only can be done on the server-side.
as #Halil Çakar said in the comment, you can emit messages directly to rooms with event namespace.
io.in(room_id).emit('your events', data)
I'm using socket.io to handle some of the server-client communication and matchmaking for a simple multiplayer game.
(for now) im automatically joining 2 players together by adding them into a socket.io room. When a room has 2 players in it I emit a "startGame" event to the room using socket.to(ROOM).emit(EVENT, EVENT_MSG) after doing a check
server side nodeJS:
game_start_state = checkRooms(socket, freeRooms);
if (game_start_state){
console.log("told room", game_start_state, "to start their game!")
socket.to(game_start_state).emit("startGame", game_start_state);
}
but so far only the first socket that gets connected to the room receives the "startGame" event message, I've looked around and havent seen anyone else with the same problem. Below is the code that is fired after the client emits an event saying it wants to join a room.
server side nodeJS:
function checkRooms(socket, roomArray) {
// auto-matchmaking logic
if(!roomArray || !roomArray.length){
//if there is no room with space create a new one
const room = uuid();
r_list.push(room); // r_list is just an array used to keep track of opened rooms for displaying to the user through some html
freeRooms.push(room); // freeRooms is an array with rooms with just 1 socket connected
joinRoom(socket, room);
return(null);
} else {
// if there is a room with a space, try to connect the client to it
const room = freeRooms[0];
console.log(socket.id, "wants to join", room);
// connect client to rooms
joinRoom(socket, room);
// room is now full so start the game
freeRooms.pop(room);
return(room);
}
}
because for now there is only auto matchmaking, there will only be 1 room in the freeRooms array so I'm not worries about this.
Does anyone know where I could be messing up? Can provide more code examples if necessary.
socket.to(room).emit(...)
sends to every member of the room EXCEPT the referenced socket. You can see that documented here.
If you want to send to everyone in the room, then use:
io.to(room).emit(...)
I'm building a game where in order to players start a game, they should press a button, when everyone in the same room clicked the buttton the game would start.
The problem is that I managed to do this only without specifying which room sent the "I'm ready message" so I'm only counting the number of connected players regardless of where they came from:
Code to give an example:
socket.on('userReady', function(){
userReadyCounter++;
if(userReadyCounter === connectionCounter){
gameDuration = 180
io.sockets.in(socket.room).emit('startGame');
io.sockets.in(socket.room).emit('newSentence', sentences[socket.sentenceCounter]);
}
})
Connection counter part:
socket.on('joinedRoom', function(roomData){
socket.username = roomData.username;
socket.room = roomData.roomname;
socket.sentenceCounter = 0;
connectionCounter++;
socket.join(socket.room);
socket.broadcast.to(socket.room).emit('userJoined', socket.username);
});
Client:
function userReady(){
socket.emit('userReady');
}
So everytime a user send the message I'm unable to tell where they came from...
Am I doing this incorrectly?
You cannot detect on the client what room sent the data. For starters, rooms don't send messages or data. A server sends the data. The server may iterate through all connections in a room and send to them, but the message is not actually sent by the room - it's sent by the server. And, the message simply doesn't include any info in it about what room it was associated with.
So, the only way you're going to know which room a message is associated with in the client is if you either created some previous association with a room so the client just knows that messages it receives are associated with a particular room or if you send the actual room that the message is associated with inside the message itself. That would be the simplest scheme:
socket.room = roomData.roomname;
socket.join(socket.room);
socket.broadcast.to(socket.room).emit('userJoined', {
user: socket.username,
room: socket.room
});
Then, every message like this that arrives on the client informs the client exactly which room it is associated with.
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.
As far i read from the doc
that Rooms are left automatically upon disconnection and they are automatically removed when everyone leaves. But this is not the case of my actual code:
io.on('connection', function(socket) {
socket.join(MainRoom);
io.sockets.adapter.rooms[socket.id].owner = socket.username;
//send the list of available rooms on connection
socket.to(MainRoom).emit('updateList',io.sockets.adapter.rooms);
socket.on('getUpdateList',function() {
io.to(MainRoom).emit('updateList',io.sockets.adapter.rooms);
});
socket.on('msg', function(msg) {
io.to(MainRoom).emit('msgFront',msg);
});
socket.on('disconnect', function() {
console.log('leaving '+socket.id);
io.to(MainRoom).emit('updateList',io.sockets.adapter.rooms);
});
});
Notice that I'm using a MainRoom where all client are forced to join it just to make sure that everyone can talk to each other.
By default Each Socket in Socket.IO is identified by a random, unguessable, unique identifier Socket#id. For your convenience, each socket automatically joins a room identified by this id.
My problem is that after closing/refreshing the browser tab, all previously joined rooms are still there, and the number of rooms is incremented(on connection the socket join new rooms automatically..)
Anyone can explain this behavior ?
Solved:
The problem is that i extended the rooms object bu adding owner attribute :
io.sockets.adapter.rooms[socket.id].owner = socket.username;
So that the extended room can't be removed. the solution that i found is to store owner attribute outside within an associative array, that's it.