How to use socket after connect event in socket.io? - javascript

sorry for my english.
There is a problem. Inside my app.js server, I added sockets, I use the join event, inside 'connection' event is a function that takes a socket as a parameter, however, I want to push the user into a room, but the name of this room is available inside the model of my REST API part of the server (from the session).
Question. How can I take and push a user who has connected to the right room inside my REST API model? (this model and its service are fired whenever a user requests a page, let's say an analogue of an authorization check). In addition to this problem, there is another service and model that are responsible for adding, for example, some kind of task, this is also a REST API, and so inside this model I would like to send messages to all the necessary users in sockets that a task has been added, EXCEPT the sender himself At the moment I can't do anything at all. It is sent to everyone in general, including the sender, the socket from the connection cannot be thrown in the REST API model
App.js
io.on('connection', (socket) => {
app.set('socket', socket);
socket.on('disconnect', () => {
socket.disconnect(true);
});
});
Controller that sends data to all services, and those in the model
const controller = (ServiceClass, params) => {
return async (req, res, next) => {
const service = new ServiceClass({
session: req.session,
sessionId: req.sessionID,
cookies: req.cookies,
socketIo: req.app.get('socketio'),
socket: req.app.get('socket'),
});
const response = await service.run(params(req));
res.json(response);
};
}
export default controller;
Inside the model that fires on every request to the site from the user, here I'm trying to give the right room
export default class IsLoggedService extends Service {
constructor(context) {
super(context);
}
async execute() {
this.context.socket
.join(`room${userSession.roleId}`);
}
}
I send information to the client about the created task also from the rest api service + model
this.context.socket
.to(`room${userSession.roleId}`)
.emit('test', 'test');
I have already reviewed the entire socket.io documentation, it says everywhere that in order to send a message to everyone except yourself, you need to use a socket, but this does not work at all, it is sent to everyone, including the sender, I also tried to achieve a socket inside the service and model, all to no avail

The most logical implementation method is that you receive all the user through the json object that you receive when sending a message through the socket and implement the logic of the program according to the data.

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

Emit event for particular user if login functionality in application in Socket.io with Node.js

I have used methods socket.on and io.emit, And i got response to all users. But, i want to get response for particular user.
But my application contains login functionality and i followed this post on stackoverflow, and they are saying we need unique userId and socketId in an object for a particular user to emit an event for a particular user.
But i am getting the userId after login, But we want it when user connect to app.
So can anyone please help me with the same?
In your node.js, create a global array 'aryUser', each element contains the socketid and loginid.
node.js onConnect (new connection), add a new element to the array with the socketid and set loginid = empty.
after the user login, emit an event from client to the server, e.g:
socket.emit('userloginok', loginid)
in node.js, define a function:
socket.on('userloginok', loginid)
and in this function, search the aryUser with the socketid and replace the empty loginid inside the array element with the parm loginid.
in node.js, define the function:
socket.on('disconnect')
and in this function, search the aryUser, use aryUser.splice(i,1) to remove the user just disconnected.
that means, aryUser contains all users connected, some of them logined, some of them not logined. And you can use the socketid of the array to send message to particular user, and/or all users.
Example Source Code:
server.js
http://www.zephan.top/server.js
server.html
http://www.zephan.top/server.html.txt
rename server.html.txt to server.html, put server.html and server.js in the same directory, and run:
node server.js
Yes, you definitely need socketId in order to send and receive messages between two specific users.
UserId is required just to keep track of socketId associated with the particular user or you can manage it with some other way as well that's up to you.
As per your question, you have userId of the user and you need socketId of that user! So, in this case, you can pass userId when that particular connects to a socket server from the client side as shown in below snippet,
const socket = io(this.SOCKET_SERVER_BASE_URL, { query: `userId=${userId}` });
And you can read this user on nodejs server like this,
const userId= socket.request._query['userId'],
const socketId= socket.id
Now store this socketId in somewhere, for example, Redis or some sort of caching mechanism again up to you, just make sure fetching and retrieval should be fast.
Now while sending a message just pull the socketId from your cache and emit the message on that socketId by using below code,
io.to(socket.id).emit(`message-response`, {
message: 'hello'
});
I have written a complete blog post on this topic on both Angular and AngularJs, you can refer those as well.
Edit 1:
Part 1 =>
When your user completes the login request, then make the connection to the socket server.
Assuming you are using React Or Angular After a successful login you will redirect your user to home component(page). On the Home component(page) make the socket server connect by passing the userId just like this,
const socket = io(SOCKET_SERVER_BASE_URL, { query: `userId=${userId}` });
P.S. you can get userID from URL or maybe using a cookie that is up to you.
Once you receive this socket connection request on the server, then you can read the userID query and you can get socketId associated with it and store it in cache like this,
io.use( async (socket, next) => {
try {
await addSocketIdInCache({
userId: socket.request._query['userId'],
socketId: socket.id
});
next();
} catch (error) {
// Error
console.error(error);
}
});
Part 2 =>
Now, let's say you have a list of the users on the client side, and you want to send a message to particular users.
socket.emit(`message`, {
message: 'hello',
userId: userId
});
On the server side, fetch the socketId from the cache using UserId. Once you get the socketId from cache send a specific message like this,
io.to(socketId).emit(`message-response`, {
message: 'hello'
});
Hope this helps.

Socket.io message after database update

Is it possible to send message (for example using alert) to all users when admin changed something in database?
situation: Users browsing car offers and while doing this admin changed price of few offers --> users gets notifications.
Just couple the event of the database update to an emit like this:
Backend
io.on('connection', (socket) => {
console.log('A user connected');
// handling event from the front-end:
socket.on('clientEvent', function(data) {
// Database update happens before this
socket.emit('databaseUpdate', { description: 'Database is updated'});
});
});
This way every time a database update happens a new event will be emitted to the frontend to all the users which are connected. Your frontend now can listen to it as follows (the frontend who is connected listened to emitten databaseUpdates from the backend):
Frontend
var socket = io();
// now we just log the updated data but in this callback you provide your own implementation.
socket.on('databaseUpdate', (data) => console.log(data.description));
Hopefully you find this answer usefull more info here
source1
Source2
You can use socket.blast() at the end of each db operation.
So, if any user is listening to the blasted message, you can make the API call so that it fetches the new record.
[http://node-machine.org/machinepack-sockets/blast][1]

get tab's socket instance on server

I have to get socket instance in my ajax request on server in node.js module. Here is my code.
app.js
io.set('authorization', function (handshake, callback) {
if (handshake.headers.cookie) {
cookieParser(handshake, null, function(err) {
handshake.sessionID = handshake.signedCookies['express.sid'];
});
} else { return callback('No cookie transmitted.', false); }
});
io.sockets.on('connection', function(socket) {
var session = socket.handshake.session;
var userid = session.userid;
socket.join("room");
//make user offline
socket.on('disconnect', function() {
//my code goes here...
//make user offline
})
});
Now in one of my ajax request, I want socket instance
app.post('/logout', function (req, res) {
//here i want socket instance, so I can emit message to all socket, accept this.
});
As I know, each tab creates it's own new socket connection, but session is unique between all tabs of browser. So, How Do I store socket for each tab on server side, where I can find easily socket instance, and then broadcast message to all sockets, excluding that socket which is creating events. (means user's active tab's socket connection)
any guess.
thanks
In my app, I can do what you say because i use namespace and room and so in a room i can find every socket of someone.
io.of('/user').clients(idRoom);
So that i can remove every socket of the user. But if you cannot use this, i think in your app you will have to implement outside socket.io a class for someone (using session as a way to see if it's already have a socket open or if you have to create a new instance). And in this class, have a socket table so that you will be able to handle socket of someone.
In my case, i do the same except that i use the room of socket.io to do that.
And to broadcast to every socket, it depends what is your app. If your app send to anyone in the same namespace, it doesn't change anything because the socket of the same session will also receive the message. But if not, you will have to implement a function to emit to every socket of the table i suggested above.
In my case i use the 'exclude' to ensure the current socket doesn't receive the message but usually you can use broadcast.
io.of('/user').in(this.id).except(socket.id).emit('msg', { text: text,type:person});
To conclude, socket.io will not help you to handle session and several socket for one user/session but you can manage to deal with it using room feature (in my case it was the best way), or implement a user class where you will manage a table of your session sockets.

Categories

Resources