How can I identify user when user reload the page - node.js

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.

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 can I redirect all clients to another page with socket.io?

I am working on an online multiplayer cards against humanity game (React on the front, React router for redirecting, and Node in the back) I am trying to redirect all clients in a "lobby", or room, when one user in the lobby clicks "start", but right now only the user that clicks start is redirected.
The idea is somebody will set up a new game, which will have a unique pin, where after you create the game and select your avatar, others can use that pin the join the lobby (this part is working fine, the users in the lobby are updated in real time for all users), and only the person who created the game will get a "start" button to click to start the game once all the users have joined, which should redirect all the users in the lobby.
On the client side, in the Lobby component's constructor, I have
socket.on("start game", () => {
this.props.history.push(`/${this.props.match.params.pin}`);
});
And for the one user who has access to the start button, the following is triggered when it is clicked
socket.emit("start");
Which then goes to the backend
socket.on('start', () => {
socket.emit('start game');
})
But only the user who clicked start is being redirected, and I cant seem to figure this out.
This is my first time trying to implement sockets and I'm not so sure what I can be missing here
The server needs to send a socket.io message to each user that you want to be redirected with the URL that they should be redirected to. Each client should have a listener for that socket.io message and when they get it, they set window.location to that new URL to cause the redirect.
So, the server would send something like this to each client that should redirect:
socket.emit('redirectToNewGame', newGameURL);
where socket is the socket of each user that should redirect.
Then, each client would have code like this:
socket.on('redirectToNewGame', newGameURL => {
// redirect to new URL
window.location = newGameURL;
});
If the whole process is started by a user clicking a button in their own window, then that user sends a message to the server to initiate the whole process. When the server receives that message, it notifies the specific set of clients that should follow that redirect by sending a socket.io message to each of them.
How exactly your server keeps track of which clients are supposed to redirect to that URL is up to your design. On the server-side of socket.io, it is common to use rooms for that, but it can be done in your own code too. That's just part of your app design and how users rendezvous before this whole process.
All that's left for it to work is to broadcast this event to all sockets in the given namespace or room. Currently you're sending it only to the socket that emitted the "start" event. Here's one way to do it:
Client side:
const socket = io("lobby", { //Connecting to a namespace called "lobby"
transports: ["websocket"]
});
socket.on("start game", () => {
this.props.history.push(`/${this.props.match.params.pin}`);
});
//...
socket.emit("start");
Server side:
io.of("/lobby").on("connection", (socket) => {
socket.on("start", () => {
io.of("/lobby").emit("start game");
});
});
A few of the people who replied recommended that I use namespaces, which makes a ton of sense, and is what worked.
io.in(pin).emit("start game");
instead of simply
socket.emit('start game');
got it to work

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.

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

How can I enforce a maximum number of connections from one user with Socket.IO?

I have a very simple Socket.IO group chat system (this one). I want to prevent people from spamming new connections from their browser using JavaScript trickery. This was a problem the one time I publicized the website.
What measure should I use to prevent this? I want to count the number of connections from a browser and it it goes over a threshold, I want to drop all connections. Should I use the IP address? Is the socket.id unique to a user? How should I do it?
If you want to look at the Socket.IO code, see the highlighted code here.
I had the same issue in a multiplayer game, to prevent a user playing through more than one tabs.
There's so many ways a client can refuse to be identified, and having their way of logging in with more than one ID. But since you only want to prevent a user in the same browser it's relatively easy.
First, use passport for authentication and passport.socketio to "bridge" req.user from Express middleware to socket.request.user
Next, since io.on('connection') will always fire for the latest socket request from the client, you can use this as an opportunity to "expire" any old sockets still connected from the same client. This is where socket.request.user will come in handy to identify whether it's the same user client. Something like this:
io.on('connection', function(socket) {
var user = socket.request.user;
// Suppose this user has connected from another tab,
// then the socket.id from current tab will be
// different from [see *1]
var current_socket_id = socket.id;
var last_socket_id = user.latest_socket_id;
if (last_socket_id) {
// If the user has an existing socket connection
// from the previously opened tab,
// Send disconnection request to the previous socket
io.to(last_socket_id).emit('disconnect');
// client-side will look like
// socket.on('disconnect', function(){
// alert('You connected from a new tab, this tab will close now.')
// window.close();
// });
}
// [*1] current socket.id is stored in the user
user.latest_socket_id = socket.id;
user.save();
});

Resources