Message sent through socket-io getting lost after a network disconnection - node.js

So I have set up a socket connection for a chat app. Under normal circumstances where both the sender and receiver are connected to the internet, the message sent between either of them passing through my NodeJS server reach's them. But if one of them is offline and a new message is sent to them, the server will send it to the user but, since the user is offline, won't accept them at that point in time. But once the user gets back online, he should be able to accept that message. How do I make this work?
Nodejs server code
io.on("connection", (socket) => {
console.log("Connected to socket io");
socket.on('setupConnection',(userData)=>{
socket.join(userData.id);
});
socket.on("newMessage", (chatData) => {
const { message,to } = chatData;
socket.in(to).emit("newMessage", { msg: message });
});
})

Related

Socket.io listener getting skipped on the client-side while there is an emitted event

I am facing this weird issue. I am not a veteran of using Socket.io. I have been exploring this library as the app I am building needs a remote playing feature wherein players create invitations to other players so that they can use those invitations to join the game remotely. I am using React on the front end (client-side), and on the server side, I am using the Nodejs Express framework with Socket.io. I have also installed client-side Socket.io for React. The basic implementation is all working fine. When there is a new user accessing the client-side app, Server-side socket.io listens to the connection. Any events triggered by the client also get reported on the server side. I am also able to broadcast the events back to all the connected clients using the socket.broadcast.emit() method.
I am trying to store the past events (basically, these are the invitations created by the currently connected players) in an array and then emit the stored array for the new connections so that the new users will see the past events(invitations). Below is my implementation on the server side:
//Array to store previously emitted events
const activeInvites = [];
//SocketIO connections
io.on("connection", (socket) => {
console.log(`⚡: ${socket.id} user just connected!`);
//Listen to the new invites
socket.on("newInvite", (invite) => {
activeInvites.push(invite);
socket.broadcast.emit("newPrivateInvites", invite);
});
//Publish all previously created invites to the new connections
io.emit("activeInvites", activeInvites); //new connections emit this event however the client won't listen to "activeInvites" event
socket.on("disconnect", () => {
console.log(`🔥: ${socket.id} user disconnected`);
destroy();
});
function destroy() {
try {
socket.disconnect();
socket.removeAllListeners();
socket = null; //this will kill all event listeners working with socket
//set some other stuffs to NULL
} catch (ex) {
console.error("Error destroying socket listeners:", ex.message);
}
}
});
And, below is my client-side implementation:
useEffect(() => {
socket.on("activeInvites", (invite) => {
console.log(invite);
}); //a new connection client skips listening to this event. Can't understand why.
socket.on("newPrivateInvites", (invite) => {
setPrivateInvites((existingInvites) => [...existingInvites, invite]);
});
//I have commented below code. Even if I uncomment it, no difference
// return () => {
// socket.off("newPrivateInvites");
// socket.off("activeInvites");
// socket.removeAllListeners();
// };
}, [socket, privateInvites]);
//Below is the handler function I use to open up a Sweetalert2 dialog to create an invite
const createMyGameInviteHandler = () => {
swalert
.fire({
title: "New Invite",
text: "This will create a new game invite and unique joining code that you can share with your friends",
iconHtml: '<img src="/images/invite.png" />',
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yeh! Let's Go!",
customClass: {
icon: "no-border",
},
})
.then((result) => {
if (result.isConfirmed) {
player.gameId = "1234";
setMyGameInvite(player);
socket.emit("newInvite", player); //This is where I create a new invitation event
}
});
};
In the above code, the "activeInvites" event is getting skipped by the new client even after socket.io on the server side triggers a new event after the new connection is created. Note that I am using io.emit() to emit the event to all the connected clients. So, even new clients should also listen. I am not able to see where the problem is. Could you please help me with this?
I tried to store the events generated by the client and consumed by the server in the past so that I could serve those events to the new clients when they establish the connection. I was expecting that io.emit() method would emit the event that will be consumed by all the clients including the new clients. However, new clients are skipping listening to this event. I am using useEffect hook in a react component.

Socket.io to room emit being received by sender, but shouldn't be?

After the client emits a message to the server, I'm trying to emit that message to every other client in the room, however the original client who sent the message also receives the emit... This seems to go completely against all documentation I've read. My code is as follows:
socket.on('slots', (slots) => {
console.log(socket.id);
const [thisRoom] = socket.rooms;
console.log(thisRoom);
socket.to(thisRoom).emit('slots', slots);
});
I'm using socket.io 4.5.3.
Many thanks.
Check this answer Send a response to all clients except the sender
// sending to all clients except sender
socket.broadcast.emit('message', "this is a test");
Please note that broadcasting is a server-only feature.
Therefore you need to configure this in the server.
io.on('connection', (socket) => {
socket.on('chat message', msg => {
socket.broadcast.emit('chat message', msg);
});
});

How to connect socket.io to a non-website application?

I made a discord bot (with discord.js) and i want to connect it with socket.io to send and receive a message from a website.
For website I use express.
How to connect socket.io to a non-website application?
If you want to use the websocket protocol to send and receive messages from a web page, you should use the built in WebSocket class. Your code can look something like this:
const socekt = new WebSocket("ws://[INSERT YOUR SERVER ADDRESS HERE]", ["wamp"]);
socket.onopen = () => {
//code to be executed, when the websocket connection was opened
console.log("Connection made!");
}
socket.onmessage = event => {
//code to be executed, when a message is recieved.
console.log(event.data);
}
socket.onclose = event => {
//code to be executed, when the connection is closed
console.log(`Connection closed with code ${event.code}.`);
}
//function to send a message via the websocket
function sendMessage(message) {
socket.send(message);
}

Websockets using angular and Nodejs: How to send data to specific client

I am trying to use websocket.io with nodejs and angular. I am trying to build a one-to-one chat application. Here is snippets of my code:
server.js
app.io.on('connection', function(socket){
console.info(`Client connected [id=${socket.id}]`);
socket.on('disconnect', function(){
console.info(`Client gone [id=${socket.id}]`);
});
});
sendmessage.js
router.post("/auth/sendMessage", function(req, res) {
//rest api to handle send message
//mongodb insert
app.io.emit('new-message', { message: message});
}
Angular Client
this.socket.on("new-message", function(data) {
if (
data.message.conversationId === self.conversationId &&
data.message.sender === "customer"
) {
self.messages.push(data.message);
setTimeout(function() {
self.messagesDiv.nativeElement.scrollTop =
self.messagesDiv.nativeElement.scrollHeight;
}, 100);
}
});
Now, the problem that I am facing is new-message will send message to all the listeners. Although, I am handling the logic on the client side, to only show the message if it is sent by specific conversationId, this should be handled at the server end only due to security issues.
So, I want to be able to send to specific users only. I think this can somehow be done using socket.id which would be unique for each connection. But how can I pass this socket.id from client to server, so that server knows on which client it needs to send the data?
There are two things here, app.io and socket. In the first snippet, you use app.io.on('connection', function(socket){. This socket is the new connection. You can store this socket object somewhere in the database or in memory.
Now, app.io.emit will send the message to all the clients, while this socket that you stored somewhere will send the message to that particular client/user on socket.emit.
In short, you need to do
router.post("/auth/sendMessage", function(req, res) {
//rest api to handle send message
//mongodb insert
socket.emit('new-message', { message: message});
}
Here is a small tic-tac-toe game I made using sockets. It will give you more insight on how to use sockets. This is not perfect but you will still understand how to use sockets.
tl;dr save socket from app.io.on('connection'... and use socket.emit to send messages to that particular client.

Prevent Socket connection from initializing again

I am trying to make a chat app which sends first message as "Hi. there".
But due to some some reasons (unstable internet connection might be one) the socket get initialized multiple times and sends the "Hi. there" message multiple times. Below is the code. How to stop the app for sending multiple messages?
io.socket.on('connect', function() {
/* On successfull connection to server */
console.log('Connected to server');
//Some code here
io.socket.get(url + '/userstatus/subscribe', function(resData, jwres) {
//Some code here
io.socket.on("userstatus", function(event) {
//Socket updartes
// Send Message code at this line.
}
}
});
You need to change your client side code, so that it stores state, and sends it to the server on reconnect. That way the server can give the correct response.
Something like this might work:
socket.on('connect', function() {
/* On successfull connection to server */
console.log('Connected to server');
socket.emit('state', state_object);
});

Resources