How to pass object references through IPC or a memory database? - node.js

I'm using the ws module here.
And to reference the socket connections to a global users object is done like so:
users = {}
var WebSocketServer = require('ws').Server, wss = new WebSocketServer({ port: 8080 });
wss.on('connection', function connection(socket) {
users[2] = {
name: 'Bob',
clientID: socket
}
});
Now, if we want to utilize this socket object that is reference to this user, we can do:
users[2].clientID.send('Hello from client ID 2');
And this will work inside the current nodejs process of which it was made.
My problem is, when sending this data through the ipc or storing this data to redis or memcache, there is no way to reference that socket back to this specific user.
For IPC, nodejs JSON.stringify's the object, and with redis and memcache it stores it as key value string pairs as well.
If I wanted to transfer this object (with the socket referenced) to another child process to be used, how can that be done?

Related

getting undefined although variable is declared on socket

This is my server side code
io.on('connection', (socket) => {
socket.username = 'john';
this is my client side
socket.on("connect", () => { console.log(socket.username); });
but I get undefined. I also tried
socket.on("connect", () => { console.log(username); });
but no use, however this here works:
socket.on("connect", () => { console.log(socket.id); });
why is that?
Properties you assign to the socket object on the server are NOT sent to the client socket object - they are unique to the server object.
Similarly, if you set a property on the client socket object, it will not be present on the server socket object.
These are different objects that exist in different processes on different computers. The socket.id property is the same on both client and server objects ONLY because the socket.io library sends the id from the server to the client as part of the connection process and the client-side socket.io library initializes that property on the client-size socket object.
If you want properties to be present on both objects, then you will have to send a message from client to server or server to client with the value of that property and then have the other end set that property on its socket object. This is not something that socket.io does auto-magically for you.
To repeat, when you do this on the server-side:
socket.username = 'john';
That does not affect the client-side socket object at all. The client and server socket objects are completely different objects running in different processes and usually on different computers communicating over a network. They do not automatically share any properties except for a few properties that the socket.io library purposely initializes to be the same in each such as the socket.id property.
You send data to the other end of the socket using something like this:
socket.emit("yourMessageName", yourData);

Pool Websocket Connections - NodeJS

I'm looking to build a node app that will accomplish the following:
Open several websocket connections, almost as if each of them were a
thread
Allow each websocket to have a unique/dynamic URL
Create a pool of websocket connections in an object based off some kind of DB query (so I can dynamically add/remove connections)
I've decided to use the ws library (https://github.com/websockets/ws) since its the fastest and least bloated option available. I currently have the following function, which only supports a single ws connection:
chat.prototype.connect = function() {
var self = this;
self.ws = new ws(url);
self.ws.on('message', function(data, flags) {
var message = JSON.parse(data);
self.handle(message);
});
};
This code listens to a single websocket URL and passes the message(s) to my handler to process the message. Instead, I want to make this function listen to multiple (potentially hundreds) of websocket URL's.
Does anyone have some ideas on how to accomplish this?
Say that you have the list of url's you need to connect to stored in an instance property called urls. You could set up the connections like this:
chat.prototype.connect = function() {
urls.forEach(this.connectOne.bind(this));
};
chat.prototype.connectOne = function(url) {
var handle = this.handle.bind(this);
var conn = this.connections[url] = new ws(url);
conn.on('message', function(data, flags) {
var message = JSON.parse(data);
handle(message);
});
};
To implement adding new connections, periodically query your database and check if each URL is already present in this.connections; if not, you can use this.connectOne() to add it. You'd do something similar to remove a connection.

Saving open sockets (or pointers to sockets) Node JS

I have a socket based server that uses Einaros WS on Node JS. I think my question applies regardless of the socket library choice. Currently, whenever I get a new connection I store each socket object in an array like this:
var WebSocketServer = require("ws").Server;
...
var server = require('http').createServer(app);
...
var wss = new WebSocketServer({server: server});
var clients = [];
wss.on("connection", function(ws) {
console.log("websocket connection open");
ws.on("message", function(message) {
{
message = JSON.parse(message);
switch (message.type) {
case "START":
{
ws.user_id = message.user_id;
clients[ws.user_id]=ws;
}
...
This means for each open connection I am storing what I believe to be a fairly large socket object. Is there a way to store an identifier or pointer to this object instead of storing the entire socket in an array this way? How do systems that can handle a very large number of open connections store / remember open sockets?
The "ws" variable is just a reference to the websocket object, not a copy of the structure, so it is no more expensive than storing any other object (or primitive) in an array.
It is better to store socket IDs in REDIS as it will help when you will scale your application and your socket servers will be on different machines.

Save Data on Socket in Socket.IO

I want to save some data on the socket, server side, so whenever the client emits any data to the server, I want that data to be available!
One use case can be storing a token on the socket. When the client is connecting for the first time, it will emit the token, if it has one, or it will show the login page and then the login data will be sent to the server. Whichever one it is, I want to store the token on the server, so that every request after that doesn't need to specify the token.
Later, I'll use RedisStore, so all the data will be accessible all the servers running the app.
My only question is, where do I store the data on the socket so it's associated with that client?
on http://socket.io/#how-to-use
scroll to: Storing data associated to a client
use socket.set and socket.get to set and get data asynchronously
I'm suffering from the same question and guessing what's going on with an example code from socket.io on version 4.x
In the example, They use middleware(use function to register a middleware)
namespace.use((socket, next) => {
// get data from client
const sessionID = socket.handshake.auth.sessionID;
const {userId, username} = yourFunction();
// set socket specific data
socket.sessionID = sessionID;
socket.userID = session.userID;
socket.username = session.username;
next();
});
Middlewares are executed when a socket is connected with a server.
and you can use the data afterward
note - Socket.IO reference tells use socket.data for this purpose
namespace.on('connection', socket => {
socket.emit("join", `${socket.username} has been joined`);
})
If you use multiple servers, then you have to keep in mind that the data is only valid for the server
On multiple server environment, You need a single source of data which will be used by socket servers.
namespace.use(async (socket: Socket & { sessionID?: string, userID?: string, username?: string }, next) => {
const sessionID = socket.handshake.auth.sessionID; // [socket.handshake][4]
// or other [socket related attributes][4]
if (sessionID) {
// you have to implement a function to save and retrive session info
const session = await someFunctionToRetrieveSession(sessionID);
if (session) {
socket.sessionID = sessionID;
socket.userID = session.userID;
socket.username = session.username;
return next();
}
}
const username = socket.handshake.auth.username;
if (!username) {
return next(new Error("invalid username"));
}
socket.sessionID = randomId();
socket.userID = randomId();
socket.username = username;
next();
});
and one more thing as I understood the namespace.use function is called only for the namespace if your client use other namespace then default then default('/') use function will not be called.
//client side
io("/chat");
...
//server side
io.use() // == io.of('/').use() will not be called
io.of('/chat').use() // only will be called
Thanksfully the author of the example implemented a sessionStorage using redis
refer to this example code
with this info, I guess socket.io server saves sockets' info in memory and set a property of a socket will be saved and when the socket comes later the server retrives the socket and it's related data. but because it happens on memory so you can't share the info among other servers that's why you have to find a way to share the data with other servers(eg. redis)
You can save the data on the global variables when you dont want to use any database
var globalVariable = {};
io.sockets.on("connection", function (socket) {
socket.on("save-client-data", function (clientData) {
var clientId = clientData.clientId;
globalVariable[clientId] = JSON.parse(clientHandshakeData);
});
socket.on("get-client-data", function (clientId) {
var clientData = globalVariable[clientId];
socket.emit("get-client-data", JSON.stringify(clientData));
});
});
This worked for my scenario, however I'm not aware of the performance implications.

Node.js - Socket.io - Express 3

I'm a noob to Node.js and Express :( I'm having an issue with accessing the socket.io object in other modules. I have created a global variable to hold a socket object however as soon as that user closes the connection the connection isn't available anymore. Technically I guess the connection still lives on but I delete it for resource reasons.
Notice below everytime a connection is made we assign that socket to the global variable which is accessible to all other modules.
// App.js
var io = require('socket.io').listen(server);
var sessionsConnections = {};
io.sockets.on('connection', function (socket)
{
global.socket = socket;
sessionsConnections[socket.id] = socket;
.....
}
socket.on("disconnect", function()
{
delete sessionsConnections[socket.id];
});
// Match.js
global.socket.emit('lobby:createMatch', data);
If the connection last assigned to the global closes Match.js will be screwed. At this point Match.js is the only module who will ever need that socket.io reference. Match.js has a bunch of exports for handling events, emitting the changes and rendering the view.
Are there any suggestions to how this is handled? Is it possible to instantiate an initial socket connection to live in App.js for the purpose of being a global reference?
The socket variable in io.sockets.on('connection', function(socket) {...}) is different for each connection.
Since in your code global.socket is always a reference to the socket relative to the last connected client, it is normal that if this client disconnects, this socket will die.
In any case, I don't see any reason to use a global variable. If you need to send a message to a specific client, you can use the socket variable inside the connection callback:
io.sockets.on('connection', function (socket)
{
socket.emit('foo', 'bar');
}
If you need to access the sockets in another module, you can export the sessionsConnections object, and access the socket you need by its id:
//In app.js
exports.connections = sessionsConnections;
//In match.js
var app = require('./app.js');
app.connections[clientId].emit('foo', 'bar');
Of course, you need to keep track of the id's somewhere.
You might try express.io,
http://express-io.org/
npm install express.io
It works the same as express, except that it has socket.io integration built-in. It also has basic routing, so that it is a little easier to deal with your issue.
Check out this simple example:
app = require('express.io')()
app.http().io()
app.io.route('some-event', function(req) {
req.io.broadcast('announce', 'someone triggered some-event')
})
app.listen(7076)
You can also do routing with objects, which makes it more like a traditional controller:
app = require('express.io')()
app.http().io()
app.io.route('posts', {
create: function(req) {
// create a post
},
remove: function(req) {
// remove a post
}
})
app.listen(7076)
Hope that helps!

Resources