Websockets using angular and Nodejs: How to send data to specific client - node.js

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.

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.

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

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 });
});
})

Is it possible to show live data from MongoDB via socket.io?

So, I would like to stream live data from MongoDB to my web, and I really don't know how can I handle live connection by sockets.io. The data is pushed to database from an external source but when I append the data on MongoDB compass nothing happened.
This is my code:
const mongo = require('mongodb').MongoClient;
mongo.connect('', function (err,db) {
if (err) {
throw err;
}
console.log("Mongodb Connected...");
io.on('connection', (socket) => {
console.log("Someone connected.");
Post.find().sort({_id:-1}).limit(1).lean().then(req =>{
socket.emit('temp', {temp: req});
})
});
});
Few things to consider while working with real-time reports
Connect your socket with server and client.
When Something happened on the server (in your case when data insert into DB) emit event as you are already doing on the above code.
Listen to your event from client side (browser) (You will get some signal or data from the server)
Once your received particular signal (event) from the client side, Hit particular URL using ajax to get data from the server and display it as per your requirements.
I Hope it will help you to solve your problem.

socket.emit issue into post method

i want to send datas in same client (not all clients) with this code;
app.post("/search", function(req, res) {
//some other codes
//if database saved {
io.sockets.emit('preview-post', {title: "blabla" });// io variable was declared as GLOBAL
// } database saved end.
res.send({bla:bla});// response end before database saving process
});
this sample is working ! but it sends to all clients , How can i emit data to same opened browser(same client) ?
Second Question is: Are there any alternative ways to do this scenario?
My algorithm is post method fired > async call to an api > response end and page loaded on client > async call to an api is still continue > if async call is finished > send alert to client . But How? i wanted to do it wiht socket .io , if i use your 3.part , it'll work , can i do this scenario any other way?
This indeed sends to all sockets.
There are a couple ways to achieve what you are looking to do. The first way is to do something like this on the server:
io.on('connection', function(socket) {
socket.on('search', function(*/ client supplied arguments /*){
socket.emit('preview-post', {title: "blabla" });
});
});
If you are insistent on using a post request, and then sending it back to the client, there are two ways to achieve this.
The easiest way, if you only need to respond to this response, is just send a standard response from node, and let the client handle it:
res.send({
event: 'preview-post',
payload: {title: "blabla" }
});
This removes socket.io's event system, so if you are insistent on using socket.io to send this event back to the same client, you are going to need to use cookies. Express and the module cookie-parser make this easy for you.
Once you have this setup, inside your request you could do something like this:
app.post("/search", function(req, res) {
var socket = findSocketByCookie(req.cookies.myUniqueCookie);
socket.emit('preview-post', {title: "blabla" });
});
function findSocketByCookie(cookie) {
for(var i in io.sockets.connected) {
var socket = io.sockets.connected[i];
if(socket.handshake.headers.cookie.indexOf(cookie) !== -1){
return socket;
}
}
}

Socket IO emitting multiple times (equal to number of clients connected) using sails js

I am trying to develop a simple socket io based chat app using sails MVC.
whenever a client connected socket emitting multiple times(equa to number of clients).
here is my code.
Server :
io=req.socket.manager;
var users=[];
io.sockets.on('connection', function(client) {
console.log("connected");
users.push(client.id);
client.on("chat", function(data) {
io.sockets.sockets[data.to].emit("chat", { from: client.id, to: data.to, msg: data.msg });
client.emit("chat", { from: client.id, to: data.to, msg: data.msg });
});
});
Client :
var socket=new io.connect('http://localhost:1337/');
socket.request('/Chat/index');
socket.emit('connection',function(data){
console.log(data);
});
socket.on('connect', function() {
console.log("Connected.");
//
});
socket.on('chat', function(data) {
console.log(data.msg );
});
please help me , is there any way to get actual socket object in sails?
I am using io=req.socket.manager; which is of req object.
the socket object should be accessible in sails.io on the server side
link
maybe you want to answer to a unique socket id? that can be achieved this way:
In the file sockets.js, at your config folder, find the onConnect property and add code like this:
onConnect: function(session, socket){
sails.io.sockets.socket(socket.id).send('YOUR_EVENT', {'message' : 'your message to a unique socket'});
}
It will send a unique message when a new connection is established. Just a couple of notes:
This can be considered a socket.io issue, not a sails issue, since the method .socket(socket.id) is part of socket.io
This was tested on sails v0.9.8, in other versions the file sockets.js might not be included in the default generated app with sails' new command
This is just an example to show how can it be done, you might need to adapt it to your needs.
Hope this helps.

Resources