attaching data to sockets - node.js

i want to have some clients connect to a room with socket.io, then store their information into an object (like a user-list)
if they leave the channel, i want that entry to be auto-deleted.
how can i hook some "outside" information to a socket? is it possible?
example:
user named "joe" connects
socket.emit('joinRoom', {username: 'joe'});
on the server, i want to do something like
socket.on('joinRoom', function(msg) {
userData.push(msg.username); // <-- how can i simplify/automatize this?
}
is there something built-in to manage users?
(the problem arises from me wanting to hook passport-user information to sockets. joe is logged in with passport, the server reqknows that. but the socket doesn't, because there is no req at all)
eventually, i want to be able to say "send a socket message to the user that is logged in as joe". if joe leaves the channel, remove him from the userlist etc
is that possible? how would you do it? is my approach wrong?
thanks

You can add properties to the socket object:
socket.on('joinRoom', function(msg) {
socket.username=msg.username;
}
If the socket you want modify is not the transmitter you can do it through
io.sockets.connected[socket.id].username=msg.username
but you will need his id.

Related

send message to specific client using soocket.io while socket id changes rapidly

I'm running a chat server using node.js and socket and want to send message to specific client.I use socket.id to send the message to the defined user,like this:
io.sockets.in(user socket.id).emit('message',message)
but there is a problem:
user remains connect but socket id changes rapidly(About once per second) so i can not use socket.id.I tried socket.join(user email) to use user email instead of socket id but after socket id changes it does not work any more.
what's the best way to solve this?session-id?If yes,how?chat application for clients runs on android device.
This is my code:
io.on("connection", function(socket) {
socket.on("login", function(useremail) {
socket.join(useremail);
});
//Here i want to send message to specific user
socket.on('messagedetection', (senderNickname,messageContent,targetuser) => {
//create a message object
let message = {"message":messageContent, "senderNickname":senderNickname}
//targetuser is the email of target user,joined to the socket in login
io.sockets.in(targetuser).emit('message',message)
});
socket.on('disconnect', function() {
console.log( ' user has left ')
socket.broadcast.emit("userdisconnect"," user has left ") });
Making my comment into an answer since it was indeed the issue:
The problem to fix is the rapidly disconnecting/reconnecting clients. That is a clear sign that something in the configuration is not correct.
It could be that network infrastructure is not properly configured to allow long lasting socket.io/webSocket connections. Or, if your system is clustered, it could be caused by non-sticky load balancing.
So, each time the connection is shut-down by the network infrastructure, the client-side socket.io library tries to reconnect creating a new ID for the new connection.

How does socket.to(username).emit('eventName',{}) work?

Can someone please explain how does socket.to(username).emit('eventName',{}) work? Basically I want to know how it identifies the 'username' is logged in or not.
socket.to(room).emit(...) will emit messages to all the users that joined room using: socket.join(room).
By default, each socket joins a room identified by the socket id, that's why, you can also do: socket.to(socketId)
Without knowing your logic, username room will be empty if the user isn't logged in, and it will have the logged user if the user is online. Socket.io doesn't really know if the user is online or not, it only knows that there is an user in that room.
io.on('connection', (socket) => {
console.log('New user connected');
const username = getUsernameSomehow(); // logged user, ip, cookie, or whatever you like
// When the user is online, it will join to
// a room named after his username
socket.join(username);
socket.on('send', (message) => {
console.log(message);
// Now you can send a message by only knowing the username
socket.to(message.target).emit('message', message.text);
});
});
If you don't join the user to username room, your code will never work.
First of all, it's not a username, it's a socket.id value or a room name that works in:
socket.to(roomname).emit(...)
The way it works is very socket that connects to your server is given a socket.id value and then that value is then added to a data structure the socket.io server keeps. Any times a socket disconnects that socket.id is removed from the data structure.
So, when you do this:
socket.to(roomname).emit(...)
socket.io looks up the roomname you pass in its internal data structure. If it's there, then it can get the socket from that data structure and can then send to it.
Because socket.io also automatically creates a room with the name of the socket.id that every socket is given, you can also do:
socket.to(socketID).emit(...)
And, using the same mechanism above, it will look for a room named with the socketID and because there is matching room for every socket that is connected, it will find that room and then send to the socket with that socketID.
socket.io itself does not have any notion of username. You would have to add that level of functionality yourself or get it from a cookie (if there's already a login cookie) when the socket.io connection is first established.

How can I identify user when user reload the page

I'm creating a card game like crazy8. And I already publish prototype.
Look here http://himapoyo.com
My problem is, when I reload the page during the game, socket is disconnected and my socket.id is changed.
So server side program can't identify me. Now, server remove player from table when socket is disconnected.(because if server don't remove the player who server can't identify, game is stopped, so I program). But if I can identify user, I don't want to remove player who just reload the page from table.
QUESTION: how can I identify user when user reload the page?
I think using cookie as ID is best for this problem. Are there Other solutions?
Other options would include:
Using local storage on the client
Passing query string values in the url
Posting the user id as part as the refresh
Storing the user in a server side session
Storing user information in redis cache.
I'm sure there are many more but this should be enough
After reading your question I understand (correct me if I'm wrong) that once a user refreshes the page (reconnects his socket) the socket ID changes (obviously) and you can't identify the user.
First of all, you shouldn't keep track of user using his socket ID as the socket ID will change every time the user reconnects. You should keep track of the socket ID for communication with the user AFTER you have identified the user.
So, what I would do is make an "authenticate" event on the server side and disconnect those sockets who don't emit the "authenticate" event. This event can expect any form of credentials (JWT, username:password etc).
After the user has been authenticated, you can take that socket ID and map that to the user ID and use the socket ID for further communication.
So the server side flow will be like this:
io.on("connect", function (socket) {
socket.on("authenticate", function(data) {
// do auth and then make other events
// if auth
socket.auth = true;
socket.emit("message", "User authenticated");
});
// If socket fails to authenticate within 3 seconds after connect, disconnect it
setTimeout(function() {
if (!socket.auth) {
socket.disconnect();
}
}, 3000);
});
After going to your app, I found that there is no authentication required. This should not be the case as I can highjack any user's slot at the table by just sending his/her name.
If you are okay with that then perhaps you can just keep track of the user name and map it to socket ID on connect.

Is there an alternate way of sending a private message with Socket.io (1.0+)?

Im working on a simple session based app shared by a session code in the URL. I decided to generate and assign a shorter user friendly unique ID for each client who connects to a socket, and the client who creates a session causes a socket.io room to be created with his ID.
I didnt realize until later that the private messaging mechanism in socket.io relied on each client being assigned to a room named by their ID. This means that because my room for a session is named after the creator's socket ID, using .to() will not message that client, but rather all of the clients now assigned to that room.
I could remedy this in ways that would require some re-design, but first I wanted to ask if there is an alternate way of sending a message to a specific client via his/her ID.
/*create an array of clients, where key is the name of user and value is its unique socket id(generated by socket only, you do not have to generate it) during connection.*/
var clients = {};
clients[data.username] = {
"socket": socket.id
};
//on server side
socket.on('private-message', function(data){
io.sockets.connected[clients[data.username].socket].emit("add- message", data);
});
//on client side
socket.emit("private-message", {
"username": userName,
"content": $(this).find("textarea").val()
});
socket.on("add-message", function(data){
notifyMe(data.content,data.username);
});

List of connected clients username using socket io

I've made a chat client with different chat rooms in NodeJS, socketIO and Express. I am trying to display an updated list over connected users for each room.
Is there a way to connect a username to an object so I could see all the usernames when I do:
var users = io.sockets.clients('room')
and then do something like this:
users[0].username
In what other ways can I do this?
Solved:
This is sort of a duplicate, but the solution is not written out very clearly anywhere so I'd thought I write it down here. This is the solution of the post by Andy Hin which was answered by mak. And also the comments in this post.
Just to make things a bit clearer. If you want to store anything on a socket object you can do this:
socket.set('nickname', 'Guest');
sockets also has a get method, so if you want all of the users do:
for (var socketId in io.sockets.sockets) {
io.sockets.sockets[socketId].get('nickname', function(err, nickname) {
console.log(nickname);
});
}
As alessioalex pointed out, the API might change and it is safer to keep track of user by yourself. You can do so this by using the socket id on disconnect.
io.sockets.on('connection', function (socket) {
socket.on('disconnect', function() {
console.log(socket.id + ' disconnected');
//remove user from db
}
});
There are similar questions that will help you with this:
Socket.IO - how do I get a list of connected sockets/clients?
Create a list of Connected Clients using socket.io
My advice is to keep track yourself of the list of connected clients, because you never know when the internal API of Socket.IO may change. So on each connect add the client to an array (or to the database) and on each disconnect remove him.
In socket.io#2.3.0 you can use:
Object.keys(io.sockets).forEach((socketId) => {
const socket = io.sockets[socketId];
})

Resources