Get notified once a net.Socket is in the CLOSED state - node.js

I wrote a simple HTTPS server written in NodeJS (v10.19):
const https = require('https');
const options = {key: ..., cert:...};
server = https.createServer(options, (req, res) => {
...
res.end("Hello!\n");
});
server.on('connection', socket => {
socket.on('close', () => {
console.log("Close event fired\n");
});
});
server.listen(5555);
I'm looking for a way to determine when a connection with a client is over.
By "over", I mean both sides of the socket are in the CLOSED state (both FIN packets, and the last ACK packet, have all been sent on the socket).
I went through all relevant NodeJS docs (HTTPS, net.Socket, stream.Duplex) and couldn't find a proper solution.
My questions are:
What is the exact definition of a "fully closed" socket the docs are referring to?
Is there a way to achieve what I want?
What I tried was listen on the net.Socket's close event, since the docs state it's
Emitted once the socket is fully closed.
In reality, the event is fired as soon as my server sends its FIN packet to the client, and before the client's FIN packet, and the server's last ACK.
What I observe:
* Server FIN *
== 'close' event fired ==
* Client FIN *
* Server ACK *
What I want:
* Server FIN *
* Client FIN *
* Server ACK *
== get notified somehow ==

Related

How to connect two mobile clients via UDP sockets

I want to make a UDP connection for voice calls between two applications. To decrease transactions over the server, I need to send the UDP packet directly from one client to another client without sending packets over the server. But I faced the below situation:
When a packet is sent to a server, the server will receive it from the router IP and a random port.
I tried to send a response from the server to the client through the router IP and port by opening a new UDP socket connection, but the client didn't receive the response.
I do the same by sending my response over the socket that already received the client message, and this time it received the client.
I failed to send UDP packets from other UDP sockets (both on the server and other clients) to my first client, even over the router IP and opened port on it.
I'm curious to know if it is possible to make client-to-client UDP connection or not?
The below code is the correct model to return respond to the client:
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
server.on('message', (msg, rinfo) => {
console.log(`* server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
const message = Buffer.from('Some bytes to '+rinfo.address+":"+rinfo.port);
//////↓///////////This is the receiver socket
server.send(message, rinfo.port, rinfo.address);
//////↑///////////
});
server.bind(8090);
By the below code, the client will not receive any response, even from the server which received the client message right now!
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
const serverResponse = dgram.createSocket('udp4');
server.on('message', (msg, rinfo) => {
console.log(`* server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
const message = Buffer.from('Some bytes to '+rinfo.address+":"+rinfo.port);
///////↓///////// This is the different socket instance
serverResponse .send(message, rinfo.port, rinfo.address);
///////↑//////////
});
server.bind(8090);
is this true? "you send a message from your client and the server receive it but when you send response to your client the client did not receive the response".
If this is true then i think that you need use a same socket object for sending and receiving because you cannot create two socket object over same port on a device.

Persiste NodeJS Sockets

Hello community,
Since the morning I am faced with an idea not to say a problem I want to store clients (sockets) in order to re-invoke them later, I know that it is not too clear I will explain in detail:
First I have a server interface (ServerSocket) net.Server() which will receive clients (sockets) net.Socket() and this one will be stored in a Map() with a unique ID for each client, and each time I want to communicate with one of them I call map.get(id).write ... or other function,
Everything works fine until I close the server, automatically the sockets will be killed ... saying that I have found a solution to store the clients for my case (vuex) or localStorage to simplify what I want is when I restart the server and I invoke one of the client it will always be active.
So my main questions:
How can I keep clients still active after the server is closed?
How can I Store sockets and check if they are active after restarting server?
var net = require("net");
var server = new net.Server();
var sockets = new Map();
/**
* This events is called when server is successfully listened.
*/
server.on("listening", () => {
console.log("Server Listen in port 4444");
});
/**
* This events is called when error occur in server
*/
server.on("error", (e) => {
if (e.code === "EADDRINUSE") {
console.log("Address in use, retrying...");
}
});
/**
* This events is called when a new client is connected to server.
*/
server.on("connection", (socket) => {
var alreadyExist = false;
sockets.forEach((soc) => {
if (soc.remoteAddress === socket.remoteAddress) {
alreadyExist = true;
}
});
if (alreadyExist) {
socket.end();
} else {
socket.setKeepAlive(true, Infinity);
socket.setDefaultEncoding("utf8");
socket.id = nanoid(10);
sockets.set(socket.id, socket);
socket.on("error", (e) => {
console.log(e);
if (e.code === "ECONNRESET") {
console.log("Socket end shell with CTRL+C");
console.log("DEL[ERROR]: " + socket.id);
}
});
socket.on("close", () => {
console.log("DEL[CLOSE]: " + socket.id);
});
socket.on("end", () => {
console.log("DEL[END]: " + socket.id);
});
socket.on("timeout", () => {
console.log("timeout !");
});
var child = sockets.get(res.id);
child.write(/* HERE I SEND COMMAND NOT IMPORTANT ! */);
socket.on("data", (data) => {
console.log("Received data from socket " + data);
});
}
});
How can I keep clients still active after the server is closed?
A client can't maintain a connection to a server that is not running. The client is free to do whatever it wants on its own when the server shuts down, but it cannot maintain a connection to that server that is down. The whole definition of a "connection" is between two live endpoints.
How can I Store sockets and check if they are active after restarting server?
You can't store sockets when the server goes down. A socket is an OS representation of a live connection, TCP state, etc... When the server goes down and then restarts, that previous socket is gone. If it wasn't closed by the server before it shut-down, then it was cleaned up by the OS when the server process closed. It's gone.
I would make a suggestion that you're asking for the wrong thing here. Sockets don't outlive their process and don't stay alive when one end of the connection goes down.
Instead, the usual architecture for this is automatic reconnection. When the client gets notified that the server is no longer there, the client attempts to reconnect on some time interval. When, at some future time, the server starts up again, the client can then connect back to it and re-establish the connection.
If part of your connection initiation is an exchange of some sort of clientID, then a client can reconnect, present it's identifier and the server can know exactly which client it is and things can then continue as before with a new socket connection, but everything else proceeding as if the previous socket connection shut-down never happened. You just rebuild your Map object on the server as the clients reconnect.
For scalability reasons, your clients would generally implement some sort of back-off (often with some random jitter added) so that when your server comes back online, it doesn't get immediately hammered by hundreds of clients all trying to connect at the exact same moment.

Socket.IO, SSL Problems With cloudflare

I'm having a socket.io app that basically receives signals from a frontend in order to kill and start a new ffmpeg process (based on .spawn()).
Everything works like expected, but often I get a 525 error from cloudflare. The error message is: Cloudflare is unable to establish an SSL connection to the origin server.
It works like 9 out of 10 times.I noticed that more of these errors pop up whenever a kill + spawn is done. Could it be the case that something block the event loop and because of this blocks all incoming requests and cloudflare logs these as a handshake failed error?
Contacting cloudflare support gives me back this info (this is the request they do to my server):
Time id host message upstream
2017-08-16T09:14:24.000Z 38f34880faf04433 xxxxxx.com:2096 peer closed connection in SSL handshake while SSL handshaking to upstream https://xxx.xxx.xxx.xxx:2096/socket.io/?EIO=3&transport=polling&t=LtgKens
I'm debugging for some time now, but can't seem to find a solutions myself.
This is how I initialize my socketIO server.
/**
* Start the socket server
*/
var startSocketIO = function() {
var ssl_options = {
key: fs.readFileSync(sslConfig.keyFile, 'utf8'),
cert: fs.readFileSync(sslConfig.certificateFile, 'utf8')
};
self.app = require('https').createServer(ssl_options, express);
self.io = require('socket.io')(self.app);
self.io.set('transports', ['websocket', 'polling']);
self.app.listen(2096, function() {
console.log('Socket.IO Started on port 2096');
});
};
This is the listener code on the server side
this.io.on('connection', function (socket) {
console.log('new connection');
/**
* Connection to the room
*/
socket.on('changeVideo', function (data) {
//Send to start.js and start.js will kill the ffmpeg process and
start a new one
socket.emit('changeVideo');
});
});
Another thing that I observer while debugging (I only got this a few times):
The text new connection displayed on the server and the connected client emits the changevideo event but nothing happens on the server side instead the client just
keeps reconnecting.
This is a simplified version of the nodejs code. If you have more questions, just let me know.
Thanks!

TCP and WEB sockets

I have http server and socket.io (listening this http server). Clients connect(via socket io) and get some information. Now I want to have clients connecting via tcp socket that will receive the same information as the clients on web socket. How to do it? Is it required to create a net server? And if so, then how information which come to http server send to tcp clients?
You need to create the TCP server so clients will be able to connect to it.
One solution can be using a messaging system (such as pub/sub with Redis, or a library like https://github.com/learnboost/kue) to notify the other server to send the data.
For example:
1) user connects to socket.io
2) user connects to TCP server
3) TCP server subscribes to listening to signals
4) socket.io emits data to the user and signals the TCP server to send the data as well
5) TCP server sends the data
in nodejs to start a tcp server:
var fs = require('fs');
var net = require('net');
var server = net.createServer(function(socket){ // create a tcp server
socket.on('data',function(data){ // on data event when data is set to the socket
var strRequestInfo = data.toString(); // get the string sent by the client
/*
here you could analyse the request data
and think what to do with it like return a certain file
*/
fs.readFile('/path/to/some/file.html', function (err, fileData) { // read a file
if (err) throw err;
socket.write(fileData); // write file content to tcp socket
});
/* -or- just write some text */
socket.write(new Buffer('some text'));
});
});
server.listen(8080, function() { // bind the server
console.log('TCP server bound');
});
you have to take in to consideration that socket.on('data') will not trigger when all the data is sent, it can trigger many time depending on the size of the data being sent.
Therefore the request data should be concatenated until the logic of your request decides to send a response back to the client.
You can add the sockets to an array if you would like to send data to all sockets:
var socketArray =[];
var server = net.createServer(function(socket){
socketArray.push(socket);
});
then you could iterate and send responses to all client:
for(var i=0;i<socketArray.length;i++)
socketArray[i].write(new Buffer('some 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