Saving socket.io data per client - javascript

How would you save data on Socket.io per client? I've thought of this, however as I'm reading it's probably invalid:
io.on('connection', function(socket) {
console.log('socket connected');
var user = {};
//do stuff with user.
});
Does this work normally, or would it mix if somebody else joins? If it doesn't work like it should, then which way does?

What you have creates a closure for each separate connection so it works fine for per socket data that you want to keep in memory. You would then need to put all other socket communication inside that same closure so that it can access the data too. The downside to this is that it's hard to get to the data from the outside (which is sometimes required, but sometimes not).
One downside of doing it this way is that the data is temporal and will not last beyond the current connection. If there's any sort of connectivity or client hiccup and the socket.io connected is dropped and reconnects, then a new closure on behalf of the new connection will be created and the original one will be garbage collected when the socket is closed.
Another way that this is sometimes done is to use the socket.id as an index into a shared data structure, or if you use any sort of authentication with a userID to use that id as the index into a shared data structure.

Related

How synchronise socketIO connection ID's on client and server?

I have a javascript GameClient that uses SocketIO to send messages to a nodeJs server. Multiple users can open the GameClient separately and send messages to the server.
GameClient
GameClient ---> NodeJS Server
GameClient
The server can send messages to specific clients using io.to(socketid).emit(). The code looks something like this:
CLIENT
this.socket = io({ timeout: 60000 })
this.socket.on('connect', () => Settings.getInstance().socketid = this.socket.id)
this.socket.on('reconnect', (attemptNumber:number) => console.log("reconnecting..."))
const json = JSON.Stringify({socketid:this.socket.id, name:"Old Billy Bob"})
this.socket.emit('user created', json)
SERVER (simplified for clarity, just keeping track of one user here)
user = {}
io.on('connection', (socket) => {
console.log('new connection')
socket.on('disconnect', () => {
console.log('user disconnected')
});
socket.on('user created', (json) => {
user = JSON.parse(json)
});
});
// demo code, send a message to our user
io.to(user.socketid).emit("message to one user")
PROBLEM
When the client browser tab becomes inactive for any reason at all, the client disconnects and reconnects and gets a new socket connection ID. This actually happens a lot in Chrome and Safari.
The server only knows the old connection id, so now it can't send direct messages any more. How do I keep the socket connection id synchronised on the client and server?
Since the server also gets a reconnected event, how does it know which user reconnected?
The answer to your question is quite simple: you need a way to identify who is who. And that is not socket.id because this only identifies sockets, not users, as you've already noticed.
So you need some authentication mechanism. Once a user authenticates you can reuse his true id (whether it is simply a name or an integer in a database is irrelevant). And then on the server side you keep a collection of pairs (true_id, socket_id). And whenever a message comes to that user, you broadcast it to all matched socket.io objects.
Edit: So here's the flow:
Client authenticates with the server, the server sends him his own true_id, which the client stores somewhere. The client may also store some session_id or maybe some other mechanism that will allow him fast reauthentication in case of disconnection (note: do not store credentials, its a security issue).
The server keeps track of (true_id, socket_id) pairs in the form of a double way, mutlivalue map (it's an implementation detail what kind of data structure should be used here, maybe two {} objects is enough). If a connection dies then (true_id, socket_id) entry is removed. Note that for a given true_id there still may be some other socket_id alive. And so it doesn't mean that the user disconnected. It only means that this particular channel is dead.
Users don't care about socket_id, they only care about true_id. What you emit is {target_id: true_id, ...} instead of {target_id: socket_id, ...} on the client side, when you want to send a direct message.
When the server receives such message with true_id inside, it retrieves all (true_id, socket_id) pairs and passes the message to all of these sockets (note: maybe you don't even need socket_id, you can simply store socket objects here). Although this is a business logic: do you allow multiple connections per user? I would. There are many edge cases here (like for example a client thinks that he disconnected, but the server thinks he is still connected, etc) and making this 100% correct is unfortunately impossible (due to the nature of networking). But with a bit of effort it is possible to make it work 99% of the time.
If a connection dies then it is your client's responsibility to automatically reconnect and reauthenticate. New socket_id for old true_id is generated on the server side.
Let me emphasize this again: clients don't care about socket_id at all. Because that doesn't identify them. This only identifies a channel. And only the server cares about this information.

Node js connecting two socket so that retrieving would be easier

Currently now i am using io.emit where i send the event to all the connected users and in client side i check whether the id of user i emit is equal to the id of client side then the condition runs i thinks its making my code messy and bit slow is there anything i can do like connecting then in one group so when retrieving them i would be easier for me.
//server side
var id = 1;
io.emit('check',id);
// on client side
socket.on('check',function(data){
var current_user_login = //getting current user login id by php
if(data == current_user_login) {
//run some code
}
});
If you want to put certain sockets in the same room, so that it's easy
to broadcast to all of them together. Try this:
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
socket.join('group');
socket.broadcast.to('group').emit('new member');
});
Hope this helps.
The better way to solve this problem is to create an association between the user you want to send the data to and that user's socket so you can .emit() only to that particular socket. This is much, much more efficient than sending to everyone especially when you have lots of connected sockets.
You would have to explain to us much more about how you know which socket or user you want to send to in order for us to help figure out how to do that association in your server.
socket.io has the concept of "rooms" which are groups of sockets that makes it easy for you to place a socket in a specific group and to then broadcast to a specific group of sockets.
Or, each socket has an id and each socket has access to the cookies that were present when the connection was first made, both of which can sometimes be used to identify which user you want to send to. But, you'd have to explain how you know which user you want to send to for us to help give you an idea how to code that into your server.

How should I manage in-memory data in Node?

I have a simple app built using Node, Express, and Socket.io on the server side. My page queries my API when it needs to retrieve data that will not change, and uses WebSockets for getting live updates from the server for dynamic data. The app allows a single person, the "Supervisor", to send questions to any number of "Users" (unauthenticated) and view their answers as they trickle in. The Users send their data to the server using a POST request, and it is streamed to the Supervisor over a WebSocket. The server stores user data in a simple array, and uses an ES6 map of the items in the array (users) to objects containing each their questions and answers, like this:
class User {}
let users = [], qa = new Map();
io.on('connection', socket => {
let user = new User(socket.id);
users.push(user);
qa.set(user, {});
socket.on('question-answered', ({id, answer}) => {
let questionData = qa.get(user);
questionData[id] = answer;
qa.set(user, questionData);
});
});
This is obviously a very primitive way of handling data, but I don't see the need for additional complexity. The data doesn't need to persist across server crashes or restarts (the user's questions and answers are also stored in localStorage), and MongoDB and even Redis just seem like overkill for this kind of data.
So my question is, am I going about this the right way? Are there any points I'm missing? I just want a simple way to store data in memory and be able to access it through client-side GET requests and socket.io. Thank you for any help.
If an array and a map provide you the type of access you need to the data and you don't need crash persistence and you have an appropriate amount of memory to hold the amount of data, then you're done.
There is no need for more than that unless your needs (query, persistence, performance, multi-user, crash recovery, backup, etc...) require something more complicated. A simple cliche applies here: If it ain't broke, it don't need fixing.

Socket.io: Emit to specified Clients

I have a node.js/socket.io webapp that is currently working correctly polling an API and populating the html page with the emitted results.
My problem is that multiple people need to use this and I would like to separate their instances so that each person will only receive the results of their query.
Right now, when anyone uses the site it will return results of another user that may be also using the site.
I have tried to get around this using this method:
var clients = {};
io.sockets.on('connection', function(socket){
console.log("this is "+socket.id);
clients.id = socket.id;
})
io.sockets.socket(clients.id).emit('progress',{info:listing});
Of course this gets replaced with each new user that logs into the site so then everything that was emitted to the original user is now being emitted to the new user.
What I want to know is if there is any built-in function to get around this or if I should proceed with another persistent store.
Any help would be greatly appreciated.
Edit
By storing the socket object in the express.sessionStore instead of just in the program.
io.sockets.on(function(socket){
request.sessionStore.socket = socket;
})
The above code now works and only emits to the event originator.
This looks like it's been answered in another thread. The idea is to create an array of clients, and associated it to some type of client/user identification, like an ID or name.
Sending a message to a client via its socket.id

Private messaging through node.js

I'm making a multiplayer (2 player) browser game in JavaScript. Every move a player makes will be sent to a server and validated before being transmitted to the opponent. Since WebSockets isn't ready for prime time yet, I'm looking at long polling as a method of transmitting the data and node.js looks quite interesting! I've gone through some example code (chat examples, standard long polling examples and suchlike) but all the examples I've seen seem to broadcast everything to every client, something I'm hoping to avoid. For general server messages this is fine but I want two players to be able to square off in a lobby or so and go into "private messaging" mode.
So I'm wondering if there's a way to implement private messaging between two clients using nodejs as a validating bridge? Something like this:
ClientA->nodejs: REQUEST
nodejs: VALIDATE REQUEST
nodejs->ClientA: VALID
nodejs->ClientB: VALID REQUEST FROM ClientA
You need some way to keep track of which clients are in a lobby together. You can do this with a simple global array like so process.lobby[1] = Array(ClientASocket, ClientBSocket) or something similar (possibly with some additional data, like nicknames and such), where the ClientXSocket is the socket object of each client that connects.
Now you can hook the lobby id (1 in this case) onto each client's socket object. A sort of session variable (without the hassle of session ids) if you will.
// i just made a hashtable to put all the data in,
// so that we don't clutter up the socket object too much.
socket.sessionData['lobby'] = 1;
What this allows you to do also, is add an event hook in the socket object, so that when the client disconnects, the socket can remove itself from the lobby array immediately, and message the remaining clients that this client has disconnected.
// see link in paragraph above for removeByValue
socket.on('close', function(err) {
process.lobby[socket.sessionData['lobby']].removeByValue(socket);
// then notify lobby that this client has disconnected.
});
I've used socket in place of the net.Stream or request.connection or whatever the thing is.
Remember in HTTP if you don't have keep-alive connections, this will make the TCP connection close, and so of course make the client unable to remain within a lobby. If you're using a plain TCP connection without HTTP on top (say within a Flash application or WebSockets), then you should be able to keep it open without having to worry about keep-alive. There are other ways to solve this problem than what I've shown here, but I hope I got you started at least. The key is keeping a persistent object for each client.
Disclaimer: I'm not a Node.js expert (I haven't even gotten around to installing it yet) but I have been reading up on it and I'm very familiar with browser js, so I'm hoping this is helpful somehow.

Categories

Resources