How to handle multiple incoming connections in Node.js? - node.js

I create a server with Node.js:
var net = require('net');
var PORT = 8181;
var server = net.createServer(
function(socket) {
console.log(this.address());
socket.on('data', function(data) {
var msg = data.toString().replace(/\n$/, '');
console.log('got: ' + msg);
});
process.stdin.on('readable',
function() {
var chunk = process.stdin.read();
if (chunk !== null) {
socket.write(chunk);
}
}
)
socket.write('heyyo\n');
}
)
Now, when multiple connections are coming in, this server sends out the typed in line only to the first connection.
I have two questions:
what is a standard way to handle this, i.e. to store the incoming sockets into an array?
exactly what happens that causes the readable event not to reach the other connections' callback function?

I would highly recommend using a library like socket.io. It makes handling connect/disconnect as well as placing sockets in rooms very simple. Additionally you can get the full list of available rooms and connected sockets through the adapter class it offers. A functional example is available in the docs.

Related

socket.io room size 4.x

So i've been making a realtime E2EE chatting application in node.js and socket.io, and I've ran into an issue
I'm using a recent socket.io version, specifically 4.2.0 and I've been attempting to find out the number of clients within a room, in order to create a check to either allow, or block access to a chat area, since it doesn't support more than 2 clients in 1 room.
My variables/includes for socket.io
var socket = require("socket.io")
var io = socket(server)
var chat = io.of('/socket').on('connection', function (socket) {
socket.on('loginchat', async function(data){
io.in(data.id).allSockets().then(result=>{
console.log(result.size) })
});
});
data.id is a randomly generated string and used to create a room server side, so my idea was to use allSockets() to get a count of sockets connected to that room, but it returns zero even with 2 clients connected.
allSockets() idea from https://stackoverflow.com/a/66065685/16140221
I can't find a working solution on stack overflow, or anything inside the docs that is an obvious method to do so. I'm not the best at socket.io since I simply picked it up to use for this project, so any help is appreciated :)
Given the example you provided, you don't seem to ever make a socket to join a room. So your room is forever empty.
According the socket.io documentation you must first listen for the connection event, then you must use join to add users to the room.
const socket = require("socket.io")
const io = socket(server)
io.on("connection", socket => {
socket.on('loginchat', async data => {
//Assuming data.id is the room identifier
const roomUsers = await io.in(data.id).allSockets();
//There, you make your socket join the room if room users are not exceeding
//your maximum
if(roomUsers.size < 2) socket.join(data.id);
});
});
var socket = require("socket.io")
var io = socket(server)
var chat = io.of('/socket').on('connection', function (socket) {
socket.on('loginchat', async function(data){
const sockets = await io.of("/socket").in(data.id).fetchSockets();
console.log(sockets.length)
if(sockets.length <= 1){
socket.room = data.id;
socket.join(data.id);
}else{
//attempting to join a room with greater than 2 users
console.log("too many")
}
});
});
https://socket.io/docs/v3/migrating-from-3-x-to-4-0/
Using the fetchSockets() and checking the length of the object, you are able to check the length or numbers of users in a room.
Working in Socket.io v4.3.1 but most likely functioning in any v4.x

Socket.io 'Handshake' failing with cluster and sticky-session

I am having problems getting the sticky-sessions socket.io module to work properly with even a simple example. Following the very minimal example given in the readme (https://github.com/indutny/sticky-session), I am just trying to get this example to work:
var cluster = require('cluster');
var sticky = require('sticky-session');
var http = require('http');
if (cluster.isMaster) {
for (var i = 0; i < 4; i++) {
cluster.fork();
}
Object.keys(cluster.workers).forEach(function(id) {
console.log("Worker running with ID : " +
cluster.workers[id].process.pid);
});
}
if (cluster.isWorker) {
var anotherServer = http.createServer(function(req, res) {
res.end('hello world!');
});
anotherServer.listen(3000);
console.log('http server on 3000');
}
sticky(function() {
var io = require('socket.io')();
var server = http.createServer(function(req, res) {
res.end('socket.io');
});
io.listen(server);
io.on('connection', function onConnect(socket) {
console.log('someone connected.');
socket.on('sync', sync);
socket.on('send', send);
function sync(id) {
socket.join(id);
console.log('someone joined ' + id);
}
function send(id, msg) {
io.sockets.in(id).emit(msg);
console.log('someone sent ' + msg + ' to ' + id);
}
});
return server;
}).listen(3001, function() {
console.log('socket.io server on 3001')
});
and a simple client:
var socket = require('socket.io-client')('http://localhost:3001');
socket.on('connect', function() {
console.log('connected')
socket.emit('sync', 'secret')
});
The workers start up fine. The http servers work fine. But when the client connects, the console logs 'someone connected' and nothing more. The client never fires the on connect event, so I think the upgrade/handshake is failing or something. If anyone can spot what I am doing wrong that would help alot.
Thanks!
#jordyyy : I was facing same issue after googling I have fond answer.
Socket.Io handshaking task complete in more than one request and when you will run on sticky session it means you are using multiple process according to your core.
So handshaking request will distribute on different different process and they can't talk.(not IPC) (They are child process) and most of time connection will be failed/lost.(connection-disconnect event occurs frequently )
So what is solution ? Solution is socketio-sticky-session
Socketio-sticky-session, manage connection on IP based. So when you will request by any client then it will maintain ip address with respect process/worker. So further request will be forward to same process/worker and your connection properly stabilized.
And When you will use redies adapter then you can actually maintain socket
connection data b/w all processes/workers.
For more information
https://github.com/elad/node-cluster-socket.io
(you need some patch on worker_index method, if your server is supporting IPv6)
Just knowledge bytes. :) :)
One more thing, you don't need to fork process. It will be done by sticky session.
This was super old and wasn't really answered when i needed it, but my solution was to drop this bad module and any other super confusing module and just use pub/sub with redis adapter. The only other step was to force transports to websockets, and if that bothers anyone then use something else. For my purposes my solution was simple, readable, didn't mess with the 'typical' socket.io api, and best of all it worked extremely well.

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.

How to write custom emitEvent for proessing in socket.io?

I am writing a multi-player game using processing.js, node.js and socket.io.
Question #1:
In the client, I use p5.js to create a class Ball.
I want the server to send parameter to create an array using that class (balls.push(new Ball(x, y));), so every client can have a bunch of balls moving on the canvas.
I know I should use socket.io to emit the parameter to the client but i have no clue.
Normally the array is created inside the setup function inside p5...so how could socket do that?
Question #2:
How could the client send the mouseX and mouseY to the server? And then how could the server send back others' mouseX and mouseY to every client?
I try to make p5.js into normal js like this:
(function () {
"use strict";
function sketchProc(processing) {
var p=processing,
var ...,
var ...;
function ball(){...}
p.setup=function(){}
p.draw=function(){}
}
var canvas = document.getElementById("canvas1"),
p = new Processing(canvas, sketchProc);
}());
But i don't know if this helps...
The balls should be passed to the client only in the connection event. That's where node's beauty lies, code sharing.
To create a socket server, you can use either node's native Net module or Socket.io
On the server:
var server = net.createServer();
server.listen(PORT, HOST);
server.on('connection', function(socket) {
socket.write({ type: "BallsArray", data: BallsArray });
});
On the client, you use WebSockets with Socket.io:
var client = io.connect('http://localhost:3000');
var BallsArray = null;
client.on('message', function(msg) {
if(msg.type == "BallsArray")
BallsArray = msg.data;
});

How can I send packets between the browser and server with socket.io, but only when there is more than one client?

In my normal setup, the client will emit data to my server regardless of whether or not there is another client to receive it. How can I make it so that it only sends packets when the user-count is > 1? I'm using node with socket.io.
To do this you would want to listen to the connection event on your server (as well as disconnect) and maintain a list of clients which are connected in a 'global' variable. When more than 1 client is connected send out a message to all connected clients to know they can start sending messages, like so:
var app = require('express').createServer(),
io = require('socket.io').listen(app);
app.listen(80);
//setup express
var clients = [];
io.sockets.on('connection', function (socket) {
clients.push(socket);
if (clients.length > 1) {
io.socket.emit('start talking');
}
socket.on('disconnect', function () {
var index = clients.indexOf(socket);
clients = clients.slice(0, index).concat(clients.slice(index + 1));
if (clients.length <= 1) {
io.sockets.emit('quiet time');
};
});
});
Note: I'm making an assumption here that the socket is passed to the disconnect event, I'm pretty sure it is but haven't had a chance to test.
The disconnect event wont receive the socket passed into it but because the event handler is registered within the closure scope of the initial connection you will have access to it.

Resources