Keep Reference to Connected Sockets Per User - javascript

I have a (for clarity's sake) a chat.
Users can login, write messages, and the others will see [name]:[message].
I don't want to send the user's name and ID every time I write socket.emit('say', message); because that's redundant, so what I'm doing on the server is like so:
var io = require("socket.io").listen(server),
sockets = {};
io.sockets.on('connection', function (socket){
socket.on('savePersonToSocket', function(user){
socket.user = user;
sockets[user.id] = socket;
}
socket.on('doSomething', doSomething);
});
// must be outside of 'connection' since I have TONS of these in a much more
//complex structure and I don't want to create them all per user connection.
function doSomething(){
...
sockets[userID].emit('foo'); // how to get the userID at this point?
...
}
So, how would I get the userID at that point?
Notes:
For each user that logs in and connects with their Facebook account, the client will tell the server to save the person's name and ID.
I thought of doing it with a cookie that saves the user's name and ID, and the server would know which user it is by reading the cookie, but that's a bit of an ugly solution: it's redundant to send that information every time.
I could also hijack the 'on' function (somehow) and add functionality that will know which user it is, because all the 'on' listeners must reside inside the 'connection' listener anyway.

What is probably happening is that a user connects to your chat application, so here
io.sockets.on('connection', function (s){
socket = s; // cache for later usage
you assign "his socket" to the variable socket which is unbound from the context, it is on its own. Now let's say a second user arrives, socket gets reassigned with the second of the second user, but if you get a savePersonToSocket event then socket will be used, which is the same for everyone and more in detail it is related to the last user that connected.
There is no need for you to keep a reference to s, you will probably have to deal with that when you will have a lot of users connecting to your application, but the solution will be very different from your approach.
EDIT: another way could be by mapping user ids and sockets:
// this assumes that the name of a user is unique
var userSocket = {};
io.sockets.on('connection', function (s){
socket.on('savePersonToSocket', function(user){
userSocket[user.name] = s;
}
So basically you are saying that you don't want to pass to doSomething event payload the userID? Because that seems like a valid solution for me, it's not redudant, it's the simplest way to let the server know about what user it is dealing with. Other solutions might be more elegant, but I doubt they are as simple and as easily maintainable as this one.

If you can save sockets under one account can sit a few people, for example, such a system would be:
var socketsList
io.sockets.on('connection', function (socket) {
socket.on('auth', function (data) {
// request to database
if ( ! (UNIQUE_ID in socketsList)) {
socketsList[UNIQUE_ID] = {};
socketsList[UNIQUE_ID][socket.id] = socket;
});
});
// ONEXIT - BROWSER EVENT
var onExit = function () { socket.emit('exit', { UNIQUE_ID : 123}) }
window.onunload = onExit;
//
socket.on('exit', function (data) {
delete socketsList[UNIQUE_ID][socket.id]
})
Correct me if I'm wrong

you can't save the connection literally, it is changing quite frequently.
however you can save the socket.id. you have io.sockets.sockets[socketid]
this is probably what you are looking for.
you can save array of ids and names and reuse them if you like.
(be aware that process related variables are make it not so scaleable. but you can don't care for it the long first time :)
also you have socket related store object socket.handshake
socket.handshake.somevariable=simple_value_a_string_or_a_number

Theoretically, something as ugly as this should work, but the usage of apply is bad IMHO, and the lack of simple function pointers makes the code uglier
var io = require("socket.io").listen(server);
io.sockets.on('connection', function (socket){
socket.on('savePersonToSocket', function(user){
socket.user = usr;
}
// No more simple function pointers....
socket.on('doSomething', function(){
// pass the Socket as the scope
doSomething.apply(socket, arguments);
});
});
function doSomething(){
...
this.emit('foo');
...
}

how about this:
var io = require("socket.io").listen(server),
sockets = {};
io.sockets.on('connection', function (socket){
socket.on('savePersonToSocket', function(user){
socket.user = user;
sockets[user.id] = socket;
}
socket.on('doSomething', function() { doSomething(socket.user) });
});
// must be outside of 'connection' since I have TONS of these in a much more
//complex structure and I don't want to create them all per user connection.
function doSomething(user){
...
sockets[user.id].emit('foo');
...
}

I know it has been a long time since you asked this, but Just 4 ago I published a module for node js, express and socket.io which manages that exactly thing you wanted. Check the Usage and Example, I hope you will find this module helpful!
You can install it via NPM socket.io.users This is a node js module for socket.io applications. One user per client.
You can loook some of the usage code on this answer

Related

Node.js, Socket.io emit not working

I am making a website in Socket.io. But emit method not working in my code. I can't see any errors in my code. Here is my server code.
var io = require("socket.io").listen(server);
//Some external codes for running server in Node.js
io.on("connection", function(socket) {
//This line is working.
console.log("Socket connected.");
io.on("requestNewMap", function(data) {
//Create new map object in here.
io.emit("responseNewMap", { mapData: map });
});
});
And this is my client side javascript code.
var socket = io();
socket.on("responseNewMap", function(data) {
var map = data.mapData;
//Draw map in canvas's context.
});
//setInterval runs this method every 2 seconds.
function requestNewMap() {
socket.emit("requestNewMap");
}
This part could be wrong:
io.on("requestNewMap", function(data) {
//Create new map object in here.
io.emit("responseNewMap", { mapData: map });
});
I would use socket there as in:
socket.on("requestNewMap", function(data) {
//Create new map object in here.
socket.emit("responseNewMap", { mapData: map });
});
I think io.emit should work fine, it would just send the response to every connected client, but io.on('requestNewMap',...) won't work since requestNewMap is not an io standard event.
change this
io.on("requestNewMap", function(data) {
//Create new map object in here.
io.emit("responseNewMap", { mapData: map });
});
into this
socket.on("requestNewMap", function(data) {
//Create new map object in here.
socket.emit("responseNewMap", { mapData: map });
});
you are adding an event listener to the server object not the client socket.
I'm not certain, but I believe your problem is that your function on the client side is written like you are expective 'requestNewMap' to be passing along data.
Instead of:
io.on("requestNewMap", function(data){
Try:
io.on("requestNewMap", function(){
OR pass an empty object or some other kind of junk data along with the emit from client side like this:
function requestNewMap() {
socket.emit("requestNewMap", {});
}
function requestNewMap() {
socket.emit("requestNewMap", undefined);
}
function requestNewMap() {
socket.emit("requestNewMap", -1);
}
Hopefully this is helpful and correct!
Edit: This turned out to not be the answer to your question, but still something worth considering. If you're not passing data along you should avoid writing the event handler as if it expects data.
This works fine only you want to send the "responseNewMap" event back to the sender only.
socket.on("requestNewMap", function(data) {
//Create new map object in here.
socket.emit("responseNewMap", { mapData: map });
});
Implement this if you want to send the event back to all connected users
socket.on("requestNewMap", function(data) {
//Create new map object in here.
io.emit("responseNewMap", { mapData: map });
});
If you use a namespace like this:
io.of('/api/v1/chat').on('connection', function (socket) {
//Your code here
});
Then include a namespace like this:
io.of('/api/v1/chat').emit("responseNewMap", { mapData: map })
I'm going to respond to this as a note to myself and others on the whole socket.emit is not working situation...
So I had issues with socket.emit not firing to the socket that made the initial request out. The issue was not an issue, more so the way client works. I was indeed emitting to the originator only when using socket.emit (which is what I wanted, only the requested got the response) but the issue was that I had more than one socket on in my app.
I use ES6 and imports (import 'path to socket io client js') which is also an issue without webpack unless you do it like that, as i have my own web component framework.
I had 3 components all creating a socket from io(), expecting them to use the same manager and they where not.
socket.io will not use the same manager over many mjs files all creating this.socket = io(); they are indeed all new isolated sockets! So take this in mind, you may be returning the message to the socket that sent the request out but if your listener is in another component than thats got a different socket.id and in essence is a whole new socket connection.
There is an option to pass in to io called forceNew which is true by default so it appears, but changing this still gave me the same issues albeit the ID's where all now the same, however I still could not seem to get a listening component to get responses from an action in an action component even though both had the same ID now. A bug maybe or something else, not sure.
So short answer is importing via ES6 without webpack needs to be just import 'fsdfsd.js'; and to save issues, only generate one socket and share between components, which you may want to do anyway as sockets are not cheap and you dont want to use many for one app!
This is my first time playing with sockets, they are great, but use sparingly and do not hold them open if you want authenticated people only, drop if not authed!
Hope this helps someone, had me lost for a day or too.
In my case, setting homepage to "." in package.json fixed the problem.

JS: Node.js and Socket.io - globals and architecture

Dear all,
Im working with JS for some weeks and now I need a bit of clarification. I have read a lot of sources and a lot of Q&A also in here and this is what I learned so far.
Everything below is in connection with Node.js and Socket.io
Use of globals in Node.js "can" be done, but is not best practice, terms: DONT DO IT!
With Sockets, everything is treated per socket call, meaning there is hardly a memory of previous call. Call gets in, and gets served, so no "kept" variables.
Ok I build up some chat example, multiple users - all get served with broadcast but no private messages for example.
Fairly simple and fairly ok. But now I am stuck in my mind and cant wrap my head around.
Lets say:
I need to act on the request
Like a request: "To all users whose name is BRIAN"
In my head I imagined:
1.
Custom object USER - defined globally on Node.js
function User(socket) {
this.Name;
this.socket = socket; }
2.
Than hold an ARRAY of these globally
users = [];
and on newConnection, create a new User, pass on its socket and store in the array for further action with
users.push(new User(socket));
3.
And on a Socket.io request that wants to contact all BRIANs do something like
for (var i = 0; i < users.length; i++) {
if(user[i].Name == "BRIAN") {
// Emit to user[i].socket
}}
But after trying and erroring, debugging, googling and reading apparently this is NOT how something like this should be done and somehow I cant find the right way to do it, or at least see / understand it. can you please help me, point me into a good direction or propose a best practice here? That would be awesome :-)
Note:
I dont want to store the data in a DB (that is next step) I want to work on the fly.
Thank you very much for your inputs
Oliver
first of all, please don't put users in a global variable, better put it in a module and require it elsewhere whenever needed. you can do it like this:
users.js
var users = {
_list : {}
};
users.create = function(data){
this._list[data.id] = data;
}
users.get = function(user_id){
return this._list[user_id];
};
users.getAll = function(){
return this._list;
};
module.exports = users;
and somewhere where in your implementation
var users = require('users');
For your problem where you want to send to all users with name "BRIAN",
i can see that you can do this good in 2 ways.
First.
When user is connected to socketio server, let the user join a socketio room using his/her name.
so it will look like this:
var custom_namespace = io.of('/custom_namespace');
custom_namespace.on('connection', function(client_socket){
//assuming here is where you send data from frontend to server
client_socket.on('user_data', function(data){
//assuming you have sent a valid object with a parameter "name", let the client socket join the room
if(data != undefined){
client_socket.join(data.name); //here is the trick
}
});
});
now, if you want to send to all people with name "BRIAN", you can achieve it by doing this
io.of('/custom_namespace').broadcast.to('BRIAN').emit('some_event',some_data);
Second.
By saving the data on the module users and filter it using lodash library
sample code
var _lodash = require('lodash');
var Users = require('users');
var all_users = Users.getAll();
var socket_ids = [];
var users_with_name_brian = _lodash.filter(all_users, { name : "BRIAN" });
users_with_name_brian.forEach(function(user){
socket_ids.push(user.name);
});
now instead of emitting it one by one per iteration, you can do it like this in socketio
io.of('/custom_namespace').broadcast.to(socket_ids).emit('some_event',some_data);
Here is the link for lodash documentation
I hope this helps.

p2p/NodeJS: Undocumented code does not tell us how to send messages through the network

We are at a standstill with a dead repository and would like to bring it back to life. Our only problem is, it's lacks so much documentation, it's almost unusable.
We are able to make a connection to the network, show when people connect, disconnect, and who all is online.
Here is the repo in question. The file to keep an eye on is /lib/node.js (not to be confused with NodeJS itself).
Here is what we have to show for it:
var Node = require('n2n').Node;
var node = new Node(5146);
console.log("Connecting to the network...\n\n\n");
node.connect([{ host: 'bradleypl.us', port: 5146 }]);
node.on('online', function () {
console.log('Your ID:', this.id);
console.log('Online ids:', node.sortedIds);
});
//just for testing, this will spam the terminal if repeated every time.
node.on('node::online', function (newNode) {
console.log('Someone is online:', newNode.id);
});
//just for testing, this will spam the terminal if repeated every time.
node.on('node::offline', function () {
console.log('Someone just left!');
});
This is where we have no idea what to so. Now how does one send messages? We can see something like:
node.broadcast("node::test","message");
Being used to send a "node::test" event to everyone on the network. That is then received with:
node.on("node::test", function (message) {
console.log("New message:",message);
}
But that doesn't work... Any idea?
It appears from a brief look at the code that you should be doing this:
node.send("test", "message")
Also there's not much there...you may be better off just rewriting what you need instead of trying to make sense of an undocumented small lib. Just my 2 cents.
Someone helped me find the solution, it appears that it emits 2 arguments when sending messages.
//node::eventName, function handles senderID and data
node.on("node::test", function (sentFrom, message) {
console.log("New message:",message);
}
Also, to send a message, you must use:
// userID, eventName, data (in this case, a string)
node.send("userid-342trw-tq34t3w-44q3t","test","Hello, World!");

Strange issue with socket.on method

I am facing a strange issue with calling socket.on methods from the Javascript client. Consider below code:
for(var i=0;i<2;i++) {
var socket = io.connect('http://localhost:5000/');
socket.emit('getLoad');
socket.on('cpuUsage',function(data) {
document.write(data);
});
}
Here basically I am calling a cpuUsage event which is emitted by socket server, but for each iteration I am getting the same value. This is the output:
0.03549148310035006
0.03549148310035006
0.03549148310035006
0.03549148310035006
Edit: Server side code, basically I am using node-usage library to calculate CPU usage:
socket.on('getLoad', function (data) {
usage.lookup(pid, function(err, result) {
cpuUsage = result.cpu;
memUsage = result.memory;
console.log("Cpu Usage1: " + cpuUsage);
console.log("Cpu Usage2: " + memUsage);
/*socket.emit('cpuUsage',result.cpu);
socket.emit('memUsage',result.memory);*/
socket.emit('cpuUsage',cpuUsage);
socket.emit('memUsage',memUsage);
});
});
Where as in the server side, I am getting different values for each emit and socket.on. I am very much feeling strange why this is happening. I tried setting data = null after each socket.on call, but still it prints the same value. I don't know what phrase to search, so I posted. Can anyone please guide me?
Please note: I am basically Java developer and have a less experience in Javascript side.
You are making the assumption that when you use .emit(), a subsequent .on() will wait for a reply, but that's not how socket.io works.
Your code basically does this:
it emits two getLoad messages directly after each other (which is probably why the returning value is the same);
it installs two handlers for a returning cpuUsage message being sent by the server;
This also means that each time you run your loop, you're installing more and more handlers for the same message.
Now I'm not sure what exactly it is you want. If you want to periodically request the CPU load, use setInterval or setTimeout. If you want to send a message to the server and want to 'wait' for a response, you may want to use acknowledgement functions (not very well documented, but see this blog post).
But you should assume that for each type of message, you should only call socket.on('MESSAGETYPE', ) once during the runtime of your code.
EDIT: here's an example client-side setup for a periodic poll of the data:
var socket = io.connect(...);
socket.on('connect', function() {
// Handle the server response:
socket.on('cpuUsage', function(data) {
document.write(data);
});
// Start an interval to query the server for the load every 30 seconds:
setInterval(function() {
socket.emit('getLoad');
}, 30 * 1000); // milliseconds
});
Use this line instead:
var socket = io.connect('iptoserver', {'force new connection': true});
Replace iptoserver with the actual ip to the server of course, in this case localhost.
Edit.
That is, if you want to create multiple clients.
Else you have to place your initiation of the socket variable before the for loop.
I suspected the call returns average CPU usage at the time of startup, which seems to be the case here. Checking the node-usage documentation page (average-cpu-usage-vs-current-cpu-usage) I found:
By default CPU Percentage provided is an average from the starting
time of the process. It does not correctly reflect the current CPU
usage. (this is also a problem with linux ps utility)
But If you call usage.lookup() continuously for a given pid, you can
turn on keepHistory flag and you'll get the CPU usage since last time
you track the usage. This reflects the current CPU usage.
Also given the example how to use it.
var pid = process.pid;
var options = { keepHistory: true }
usage.lookup(pid, options, function(err, result) {
});

Node.JS Socket.IO sending packets to specific connection IDs

I have been searching for this particular problem for the past week, and since I couldn't find any information on the subject(that wasnt outdated), I just decided to work on other things. But now I am at the point where I need to be able to send data(that I constructed) to specific clients using their ID who are connected to my server using node.js and socket.io. I already store the ID in an object for each new connection. What I need to know is a way to just send it to a connection ID I choose.
Something like: function send(data, client.id) {};
I am using an http server, not TCP.
Is this possible?
edit:
server = http_socket.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(respcont);
client_ip_address = req.header('x-forwarded-for');
});
socket = io.listen(1337); // listen
//=============================================
// Socket event loop
//=============================================
socket.on ('connection', function (client_connect) {
var client_info = new client_connection_info(); // this just holds information
client_info.addNode(client_connect.id, client_connect.remoteAddress, 1); // 1 = trying to connet
var a = client_info.getNode(client_connect.id,null,null).socket_id; // an object holding the information. this function just gets the socket_id
client_connect.socket(a).emit('message', 'hi');
client_connect.on('message', function (data) {
});
client_connect.on ('disconnect', function () {
);
});
solution: I figured it out by just experimenting... What you have todo is make sure you store the SOCKET, not the socket.id (like i was doing) and use that instead.
client_info.addNode(client_connect.id, client_connect.remoteAddress, client_connect, 1)
var a = client_info.getNode(client_connect.id,null,null,null).socket;
a.emit('message', 'hi');
If you need to do this, the easiest thing to do is to build and maintain a global associative array that maps ids to connections: you can then look up the appropriate connection whenever you need and just use the regular send functions. You'll need some logic to remove connections from the array, but that shouldn't be too painful.
Yes, it is possible.
io.sockets.socket(id).emit('message', 'data');
Your solution has one major drawback: scaling. What will you do when your app needs more the one machine? Using internal IDs also could be difficult. I advice using external IDs (like usernames).
Similarly to this post I advice using rooms (together with Redis to allow scaling). Add private room for every new socket (basing on user's name for example). The code may look like this:
socket.join('priv/' + name);
io.sockets.in('priv/' + name).emit('message', { msg: 'hello world!' });
This solution allows multiple machines to emit events to any user. Also it is quite simple and elegant in my opinion.

Categories

Resources