Socket.io + express rooms - javascript

I am working on an online website with real-time chat and I've been trying to set up socket.io rooms for a few days now, but one of my custom events just doesn't emit.
My website has 2 paths, / and /play. When someone fills out a form in the / path, the website redirects them to the /play path.
const io = require('socket.io')(listener);
io.on('connection', socket => {
socket.on('add user', data => { // This event gets emitted when the user submits the form at `/`, in a client-side js file.
socket.username = data.username;
socket.room = data.room;
socket.score = 0;
socket.join(data.room);
io.sockets.in(data.room).emit('user joined', { // This event gets 'handled', in another (not the same one) client-size js file.
username: data.username,
room: data.room
});
});
socket.on('disconnect', function () {
io.sockets.in(socket.room).emit('user left', {
username: socket.username,
room: socket.room
});
socket.leave(socket.room)
});
});
In the other client-side js file:
var socket = io.connect();
let joined = 0;
socket.on('user joined', data => {
joined++;
console.log(joined, data)
const elemental = document.createElement("LI");
const info = document.createTextNode(data.username);
elemental.appendChild(info);
document.getElementById('people').appendChild(info)
});
The add user event code executes just fine, but the user joined one doesn't..
I'm almost certain that it has to do with the paths. I read on socket.io namespaces but it seems like they are just like a different type of rooms.
Can someone tell me what's wrong?
EDIT: I should mention that no errors come up.

Without seeing your whole code, I'm going to assume that the socket on client-side.js code, is not joined to the data.room room, and that's why you don't get anything on the 'user join' event listener and that happens because you probably have multiple socket.io connections on the client side code.
You have multiple ways to solve this, depending on what you're trying to achieve.
Instead of only emitting to people on the room, emit to all sockets that an user joined a specific room. io.emit('user joined') instead of io.sockets.in(data.room)...
There should only be one single var socket = io.connect(); on the front end, otherwise the socket that's emitting: add user and therefore joined to data.room is not the same socket that's listening on: user joined, and that's why you never get the event on that socket, because you have 2 different sockets, one that joined the room, and one that is doing absolutely nothing except waiting for an event that will never occur.

Related

Socket.io emitting to socket.id when stored in variable not working

as given in Sending message to a specific ID in Socket.IO 1.0, it is possible to emit to a specific client id by using
io.to(socketid).emit('message', 'for your eyes only');
In my node.js application, I am attempting to do the same thing. Basically, when the user submits another's socket.id, the node.js backend is to send the data given to that specific socket id. While the front-end submits the request correctly to the backend, when I attempt to send the data to the id, it does not go through. The "broken" part of the code looks like this:
app.post('/send', function (req, res) {
var post_body = req.body;
var id = (JSON.stringify(post_body.id)).split('"')[1].split('"')[0];
var payload = JSON.stringify(post_body.payload);
var user = JSON.stringify(post_body.user);
console.log(id);
console.log(payload);
console.log(user);
io.to(id).emit('testerEvent', { description: 'A custom event named testerEvent!'});
res.status(200);
});
which is responding to the posted data (data is posted correctly). The client listens for the event 'testerEvent' as follows:
socket.on('testerEvent', function(data){document.write(data.description)});
When the event testerEvent is fired with just io.emit, and not io.to(id).emit, it works fine.
I would appreciate any help on this, as I am just beginning to learn node and socket.io
io.to(id) will send a message to clients that joined a room, so if you have not joined any clients to a room you won't receive the message on a client. To resolve the problem you may try to do client.join(id) when you receive a client socket from Socket.io.

I can't figure out why my Socket.io listener triggers once but then doesn't trigger even if more emits come from the back-end

I'm making a clone of discord and right now I'm trying to implement the online/offline functionality of the server users. I'm trying to implement it like this:
When a user joins a server, I emit a userCameOnline event with the username of the user:
state.socket.emit('userCameOnline', state.username)
Then on the back-end, I listen for that event and once I receive it, I set the socket's username to the emitted username, then push that username to an array of online users and finally, I emit back an event called onlineUsers:
socket.on('userCameOnline', (username) => {
socket.username = username
onlineUsers.push(socket.username)
console.log(onlineUsers)
socket.emit('onlineUsers', onlineUsers)
})
And this is the onlineUsers listener on the front-end that sets onlineUsers property to the server:
state.socket.on('onlineUsers', (onlineUsers) => {
console.log(onlineUsers)
server.onlineUsers = onlineUsers
})
Now here's the problem. The onlineUsers listener works when I load the page the first time, however, when I open a second browser and join the chat with another account, the client emits these events again with the new user:
state.socket.emit('userCameOnline', state.username)
I know this is working fine as I console.log(onlineUsers) on the back-end and see that the array indeed has 2 users once the second client has joined. This means that this works:
socket.on('userCameOnline', (username) => {
socket.username = username
onlineUsers.push(socket.username)
console.log(onlineUsers)
socket.emit('onlineUsers', onlineUsers)
})
Unfortunately, the onlineUsers listener on the first client doesn't trigger which means that the onlineUsers property of the server doesn't get updated with the newly joined user. I've been banging my head for some time now and I can't figure out why is this happening. I've been following the trail and console.logging everything but I still can't figure it out.
This is why I am wondering why does this:
state.socket.on('onlineUsers', (onlineUsers) => {
console.log(onlineUsers)
server.onlineUsers = onlineUsers
})
Triggers once I load the page and then doesn't trigger anymore even though I'm emitting the event from the back-end every time a new user joins the server?
So, if i understand your problem then instead of emitting in the Back-end:
socket.emit('onlineUsers', onlineUsers)
You should emit :
io.emit('onlineUsers', onlineUsers)
Because by emitting with socket.emit() you will end up emitting only to the same client that emitted to the server in the first place.
As with io.emit() you will emit to all of your clients.
Assuming that you are using something like :
const io = require('socket.io')(server, {(your_args)})
If you want to check for further information on how to use the emit function
check out this page https://socket.io/docs/emit-cheatsheet/ from their website.
Finally i would highly recommend you to check out their API documentation and other blogs about it

Socket.io matchmaking

I'm working for a project in which i have to make a multiplayer battleship game, i've already completed most of the game logic (Angular 6 for the client and node.js + express for the server side), but i'm currently stuck on this since i'm new to socket.io, i've managed to get this down to try and connect 2 players :
var waitingPlayer = null;
ios.on('connection', (socket) => {
console.log('new user connected');
if (waitingPlayer == null) {
waitingPlayer = socket;
waitingPlayer.emit('waitingOpponent', {
text: "waiting for an opponent"
});
} else {
console.log('Match Found');
waitingPlayer.emit('matchFound', {
text: "Match Found",
});
socket.emit('matchFound', {
text: "Match Found"
});
}
});
If i try this it seems more than 2 users can connect together, is there a way to make some sort of lobby just between the 2 users that will be playing in the same game and then make more for the ones that will connect after? I've looked up some documentation this but couldn't find much.
Sounds like a good situation to use a Room with Socket IO. You can create rooms on the fly, and Socket IO basically manages them for you. So, you could create a room for each individual "Battle" or game. Put the two users in that games room. After that, you can broadcast to that room specifically. You will need to track the rooms somehow, but this should give you a starting point. Perhaps if each game that occurs has its own unique ID, the room name can be that ID. Then, you can broadcast to that specific room ID when needed.
Joining:
var gameId = "game_1234";
io.on('connection', function(socket){
socket.join(gameId);
});
Emitting:
io.to(gameId).emit('some event');
https://socket.io/docs/rooms-and-namespaces/

Socket.io, emit to a single client

I have a chat app that allows multiple people to connect to a chatroom, When a new client connects to the chatroom i want to be able to show them the chat history, i've done his my emitting an chat history but obviously that updates everyone in the chat leading to duplicate messages.
How would i single out the new user and display the chat just to them?
this is the server code
socket.on('chat history', function(){
console.log("Chat History");
posts.find({chatId: chatId}).then(function(chatHistory){
console.log("Chat history", chatHistory);
socket.emit('chat history', chatHistory);
})
});
This is the client side code
socket.on('chat history', function(chatHistory){
for (var key in chatHistory) {
var obj = chatHistory[key];
addChatMessage(obj);
}
});
I am using the socketio example, the data is sending back to this method and the add chat message updates everyone.
In order to communicate to an individual, you'll require to capture their ID. You can do this by accessing the socket object, assuming that's the name of what you're creating on the connect action as illustrated in this block of server-side code:
io.on('connection', socket => {
socket.on('chat history', async function(){
console.log("Chat History");
const chatHistory = await posts.find({chatId: chatId})
console.log("Chat history", chatHistory)
socket.to(socket.id).emit('chat history', chatHistory)
})
})
In your example I've swapped the promise for async/await for the sake of readability, but you can accomplish the same using your preferred pattern. Just trust in this case that the ID which you seek to capture will always be kept to that context within the socket object that you generate on connection.
You can use below code line to send event/message to particular user/client:
socket.broadcast.to(socketid).emit('chatHistory', data);

Socket.io, message to yourself

Socket.io doesn't display messages send on yourself ip.
For example
var id = 333;
socket.broadcast.to(id).emit('user', user);
It working good, but message is only in client #333, but user than sent message, do not have a copy in the message client.
I wanted to solve in this way, but it does not work
socket.broadcast.to(socket.id).emit('user', user);
Why?
Without more code its hard to say what you want but one thing is certain in order to send a message to a single user you must use that socket object and use socket.emit
As far as i know broadcast is only used to tell everyone except for yourself.
What i usually do when it comes to keeping track of users is i have the following:
var userList = [];
io.on('connection', function (socket) {
socket.on('userData', function (userDetails) {
userDetails.socket = socket;
userList[userDetails.id] = userDetails
});
});
Basicly when a user connects to my socket and the page for the user is fully loaded it sends its id (or a token if you wish) i then map the user's socket into the list so i can quickly pick it up again if i wish to send to that user.
An example could be:
user.id = 33 connects to our server
Once loaded the users emits to our server userData function
The socket is then taken and put into the list at row 33
When we need to we can this use the following code to get the users socket:
socket = userList[33];
or if we have the object:
socket = userList[user.id];
I hope this helps you.
For this, you can use socket.emit('message').
socket.emit: Emit for only one socket.
Hope this will help you. You can also check out this link: socket.io send packet to sender only

Categories

Resources