I'm currently working on a small node.js game.
Game supposedly has a global chat, with a "logged in" list to challenge people.
For the chat and the logged in list, i'm using the default socket.io room/namespace.
I successfully send the challenge request with following code
// When a user sends a battle challenge
socket.on("sendChallenge", function(data) {
// Try with room
socket.join('battleRoom');
var data= {
"userID": data.targetID,
"challengerName": data.challengerName
};
console.log(data.challengerName + " challenged " + data.userID);
// broadcast the message, but only the concerned player will answer thanks to his ID
socket.broadcast.emit('receiveChallenge', data);
});
Client side, I then have this code :
socket.on('receiveChallenge', function (data) {
if (data.userID == userID) {
alert("received challenge from " + data.challengerName);
socket.emit('ack');
}
});
The right player indeed receive the alert, and sends 'ack' to the server :
socket.on('ack', function() {
socket.join('battleRoom');
socket.to('battleRoom').broadcast.emit('receiveMessage', 'SYSTEM: Battle begun');
//socket.to('battleRoom').emit('receiveMessage', 'SYSTEM: Battle begun');
})
Except that the "socket.to('battleRoom').broadcast.emit('receiveMessage', 'SYSTEM: Battle begun');" is only received by the challenger and not by the challenged, and I'm stuck.
(the 2nd line is commented because challenger received 2 messages and challenged received none)
The way i understand it, on the server, the functions socket.on() have "socket" as the client that sent the message, and then you use broadcast to send to all others.
Why is why, in the sendChallenge event, i have the socket.join('battleRoom') for the challenger to enter battleRoom.
I then broadcast the challenge, and asks the client to acknowledge the challenge.
In the ack, the client then supposedly joins battleRoom too.
But i'm obviously doing something wrong, and i can't seem to see what...
I want both the challenger and the challenged communicating through the server.
A link to an image more or less showing the situation : Here
(screen is during the second time i clicked on the quickFight button, showing the Battle begun of the 1st click on the left, and the alert that user was challenged on the right)
Thanks in advance for your help!
As per the socket.io documentation broadcast does the following:
Broadcasting messages
To broadcast, simply add a broadcast flag to emit and send method calls. Broadcasting means sending a message to everyone else except for the socket that starts it.
So when you do:
socket.to('battleRoom').broadcast.emit('receiveMessage', 'SYSTEM: Battle begun');
You do not emit to the socket that broadcasted to the room. Simply replace that line with the following:
io.to('battleRoom').emit('receiveMessage', 'SYSTEM: Battle begun');
This is assuming that you named your socket.io object io, if you named it differently use that.
Just as a side-note, it's not necessary here to broadcast your original message to everyone if you create an associative array with all your connected socket id's (or sockets themselves). Then you can find the challenged socket id in your array and only emit to them specifically with the challenge. This would cut down on your server calls.
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 am unable to get two users chatting to each other despite reducing the complexity and the potential code that could have caused the issue.
I am able to emit to all connected sockets so I have established it's not an issue in context of emit/on structure but rather; coming from the way i'm handling the private socket ids.
I have tried various versions of trying to send the private message to the correct socket id; I have tried older ways such as socket.to and the current way from the docs which is io.to(sockid).emit('event' message); all these variations have been unable to help me. I have consoled out the socket id I have on my Angular client side by printing console.log('THIS IS MY SOCKET '+this.socket.id) and comparing it to the value I have in Redis using redis-cli and they both match perfectly every time which doesn't give me too much to go on.
problem arises here:
if (res === 1) {
_active_users.get_client_key(recipient)
.then(socket_id => {
console.log('======='+io.sockets.name)
console.log('I am sending the message to: '+ recipient + 'and my socket id is'+ socket_id)
// socket.to(socket_id)socket.emit('incoming', "this is top secret"
io.of('/chat').to(socket_id).emit('incoming', "this is top secret")
})
.catch(error => {
console.log("COULD NOT RETRIEVE KEY: " + error)
})
Here is the link to the pastebin with more context:
https://pastebin.com/fYPJSnWW
The classes I import are essentially just setters and getters for handling the socket id you can think of them as just a worker class that handles Redis actions.
Expected: To allow two clients to communicate based on just their socket ids.
Actual:
am able to emit to all connected sockets and receive the expected results but the problem arises when trying to send to a specific socket id from a unknown reason.
Issue was coming from my front end.. I hope nobody has a headache like this! but here is what happened; when you're digging your own hole you often don't realise how deep you got yourself if you don't take the time to look around. I had two instances of the sockets. I instantiated both and used the one to connect and the other to send the message; which of course you cannnot do if you want things to work properly. So what I did was created only one instance of the socket in and and passed that ref of the socket around where I needed it which is ( sendMessage(username, socket) getMessage(socket)
ngOnInit(
this.socket = io.connect('localhost:3600',{
reconnection: true,
reconnectionDelay: 1000,
reconnectionDelayMax : 5000,
reconnectionAttempts: Infinity});
my question is about socket rooms and leave and joining in rooms .my problem is that : i am creating chatroom and i want whenever user (socket) clicks "next person" button (in my case link) i want socket to disconnect (leave) its current room and join another one where another socket is waiting for second socket. like in Omegle
Heading
so i tried this code
client-side:
N.addEventListener('click', function(e){
socket.emit('next')
});
socket.on('next',function(){
socket.disconnect()
document.GetElementById('divd').style.display = "inline";
socket.connect()
});
server-side:
socket.on('next', function(){
socket.leave(socket.current_room)
chnm.in(socket.current_room).emit('next');
socket.join(room)
});
but whenever i click "next" it only shows both socket disconnect div like in disconnect event but does not lets socket which triggered that event to join other room
rooms in my case is like that
var room = "room" + numb;
socket.current_room = room;
but what i want is to show in room (where socket disconnected) that socket disconnected and socket which triggered that event to be joined in other room .
(example: in room 1 socket triggered "next" link he/she disconnects from the room 1 and joins to room 2 and in room 1 appears disconnect div, i think it will appear anyway if i use socket.disconnect() because i already created disconnect event . thanks guys for help <3
There's still not enough code shown or enough of the overall design described that you're trying to achieve, but here's what I can see from what you've shown so far.
Here's your sequence of events:
User clicks button
Client code does socket.emit('next')
Server receives next message
Server calls socket.leave(socket.current_room) to leave the current room
Server does a .emit('next') to everyone still in that room
Server calls socket.join(room) (I have no idea where the room variable comes from)
Clients receive next message and that causes them to socket.disconnect() and thensocket.connect()` (no idea why it's doing that).
The things that seem odd to me about this sequence of events are:
The socket.disconnect() followed by the socket.connect() is completely unnecessary and probably causing problems. First off, these events are asynchronous. If you really wanted to do one followed by the other, you need to wait for the disconnect to finish before trying to do the reconnect. But, mostly, there should be no reason to disconnect and then reconnect. Just update the state of the connection you already have.
I don't follow why when you assign someone to a new room, you then disconnect everyone else who was in that room.
Please don't use the same message name next to mean one thing when the client receives it and something completely different when the server receives it. This isn't a programming error per se, but it really makes your code hard to understand. Give each its own descriptive name.
It seems that I'm not the only one struggling with this problem.
However I am trying to write a simple chat application. When a user joins a room, the roomID is saved in socket.room.id. It is just a number.
When they join / change rooms, I have the following code:
// Notify the rooms
if(previousRoomID) io.to(previousRoomID).emit("activity-notification","<b>"+socket.me.name+"</b> has left the room " + previousRoomID);
io.to(socket.room.id).emit("activity-notification","<b>"+socket.me.name+"</b> has joined the room " + socket.room.id);
Now, Let's say that previousRoomID = 1, and socket.room.id = 30.
It sends the message, but they both seem to go to all users. If I am in roomID 1, and a user leaves the room, I get the following messages:
user has left the room 1
user has joined the room 30
I shouldnt be able to see the second message, because in the code above I am sending it to io.to(socket.room.id).emit(), yet even when I am in room 1 I am getting this message.
Strangely enough also, It seems to be using the broadcast functionality as well, even though I am not calling it; as in - it sends it to all users except the one who fired the event.
What am I doing wrong?
Okay so I have this figured out. The problem was I was calling
socket.join(roomID)
But I wasn't leaving a room first. So it turns out that a socket can join multiple rooms, which would explain why I was seeing both the messages.
if(previousRoomID) socket.leave(previousRoomID);
socket.join(roomID)
if(previousRoomID) io.to(previousRoomID).emit("activity-notification","<b>"+socket.me.name+"</b> has left the room");
io.to(socket.room.id).emit("activity-notification","<b>"+socket.me.name+"</b> has joined the room");
I dont understand the broadcast functionality though, in theory the messages should be going to everyone but for some reason the socket that calls the io.to.emit line doesnt get the activity-notification.
Is it possible to get the socket.id from the client thats emits something?
Like:
Clientside:
socket.emit("lol", "data")
Server side:
socket.on("lol", function(data) {
// get the socket.id from the client who sended this!!
});
So, I`m making a Tic Tac Toe game in multiplayer. I'm using rooms. In each room there are 2 players. But I need to detect in which room the player is that clicked a field.
I can also explain it like this:
Server side:
player1.on("click", function(data) {
console.log(data + " in room" + playRoom);
});
But this is not working.
I'm going to use answer format, but this may be only directionally correct :) If you want to send data to a room, the client has to specify what room to send to. For example:
socket.broadcast.to('room').emit('event_name', data);
To join a room:
socket.join('room');
This SO answer has a nice way of showing how a single socket could emit to multiple rooms (but I don't see the emitter actually subscribed to the room): https://stackoverflow.com/a/16475058/1861459
To your actual question of the client's ID - I might debug through socket.rooms to see if you can divine what specific ID each room's subscription has: https://github.com/Automattic/socket.io/wiki/Rooms#rooms-a-client-has-joined however, I don't see how this helps you - if you join a room and broadcast to a room the library is taking care of the namespacing for you.