Get Socket Ref On Socket Close
Following this SO answer, I expect to be able to shutdown the server and restart it again. To do that accountably, it sounds like I need to not just call close() on the server instance object but also any remaining open sockets. Memory Leaks are an issue so I would like to store the socket instances in a Set and clean up expired connections as they close.
You need to
subscribe to the connection event of the server and add opened sockets to an array
keep track of the open sockets by subscribing to their close event and removing the closed ones from your array
call destroy on all of the remaining open sockets when you need to terminate the server
...
const sockets = new Set(); // use a set to easily get/delete sockets
...
function serve(compilations) {
const location = './dist', directory = express.static(location);
const server = app
.use('/', log, directory)
.get( '*', (req, res) => res.redirect('/') )
.listen(3000)
;
server.on('connection', handleConnection); // subscribe to connections (#1)
console.log(`serving...(${location})`);
return server;
}
...
function handleSocketClose(socket, a, b, ...x) { // #param#socket === false (has-error sentinel) (#2.2)
const closed = sockets.delete(socket);
console.log(`HANDLE-SOCKET-CLOSE...(${sockets.size})`, closed, a, b); // outputs "HANDLE-SOCKET-CLOSE...(N) false undefined undefined"
}
function handleConnection(socket) {
sockets.add(socket); // add socket/client to set (#1)
console.log(`HANDLE-CONNECTION...(${sockets.size})`);
socket.on('close', handleSocketClose); // subscribe to closures on individual sockets (#2.1)
}
From the SO answer (above), I have #1 & #3 completed. In fact, I even have, say, #2.1 done as I am currently listening on each socket to for the close event -- which does fire. The problem is, say, #2.2: "...and removing the closed ones from your array".
My expectation while implementing the handleSocketClose callback was that one of the argument values would be the socket object, itself, so that I could then cleanup the sockets collection.
Question
How in Laniakea can I obtain a reference to the socket in question at close time (without iteration and checking the connection status)?
Related
I have an application in which my TCP server module (parent) listens for 'connection' events and receives some data on the created socket to perform a handshake with the remote client. Once the handshake is performed, the server needs to send the socket object to a forked child, which will also send and receive data to the socket, do some stuff and finally send result to parent and be killed. For some reasons, I need to keep the socket object in the parent for further data processing not performed in the child, after the child has finished.
I've managed to send the socket to the child using the subprocess.send() method but, this way, the socket handle becomes null in the parent. I tried setting the keepOpen option to true and it almost worked, since I can send the socket and still work with it in the parent, but It seems not to work properly, because incoming data is not always received by the child 'data' event listener.
I also tried to removeListener for the 'data' event from the parent, prior to sending the socket to the child, but this made no difference, data is still being lost at some point on some occasions (on some others it is correctly received after an unexpected delay...). This code extract illustrates what I'm trying to do:
const net = require('net');
const server = net.createServer();
const cp = require('child_process');
server.on('connection', (socket) => {
socket.on('data', (data) => {
// Perform handhsake
const child = cp.fork('child.js');
child.on('message', (result) => {
console.log('CHILD finished processing: ', result);
child.kill('SIGHUP');
// Do more stuff with socket
});
child.send('socket', socket);
// (At this point, socket handle is null)
});
});
server.listen(PORT)
I'm new to nodejs, I assume there might be errors in the code. Thanks.
I know nodejs is asynchronous by nature and it is preferable to use that way, but I have a use case where we need to handle incoming TCP connections in synchronous way. Once a new connections received we need to connect to some other TCP server and perform some book keeping stuff etc and then handle some other connection. Since number of connections are limited, it is fine to handle this in synchronous way.
Looking for an elegant way to handle this scenario.
net.createServer(function(sock) {
console.log('Received a connection - ');
var sock = null;
var testvar = null;
sock = new net.Socket();
sock.connect(PORT, HOST, function() {
console.log('Connected to server - ');
});
//Other listeners
}
In the above code if two connections received simultaneously the output may be (since asynchronous nature):
Received a connection
Receive a connection
Connected to server
Connected to server
But the expectation is:
Received a connection
Connected to server
Receive a connection
Connected to server
What is the proper way of ding this?
One solution is implement a queue kind of solution with emitting 'done' or 'complete' events to handle next connection.
For this we may have to take the connection callback out of the createServer call. How to handle scoping of connection and other variables (testvar) in this case?
In this case what happens to the data/messages if received on connections which are in queue but not yet processed and not yet 'data' listener is registered.?
Any other better solutions will be helpful.
I think it is important to separate the concepts of synchronous code vs serial code. You want to process each request serially, but that can still be accomplished while handling each request asynchronously. For your case, the easiest way would probably be to have a queue of requests to handle instead.
var inProgress = false;
var queue = [];
net.createServer(function(sock){
queue.push(sock);
processQueue();
});
function processQueue(){
if (inProgress || queue.length === 0) return;
inProgress = true;
handleSockSerial(queue.shift(), function(){
inProgress = false;
processQueue();
});
}
function handleSockSerial(sock, callback){
// Do all your stuff and then call 'callback' when you are done.
}
Note, as long as you are using node >= 0.10, the data coming in from the socket will be buffered until you read the data.
A net.Socket object in NodeJS is a Readable Stream, however one note in the docs got me concerned:
For the Net.Socket 'data' event, the docs say
Note that the data will be lost if there is no listener when a Socket emits a 'data' event.
That seems to imply a Socket is returned to the calling script in "flowing-mode" and already un-paused? However, for a generic Readable Stream, the documentation for the 'data' event says
If you attach a data event listener, then it will switch the stream into flowing mode, and data will be passed to your handler as soon as it is available.
That "If" seems to imply if you wait a bit to bind to the 'data' event, the stream will wait for you, and if you intentionally want to miss the 'data' events, the example in the resume() method seems to indicate you must call the resume() method to start the flow of data.
My concern is that when working with a net.Server, when you receive a net.Socket as part of a 'connection' event, is it imperative that you start handling the 'data' events right away since it's already opened? Meaning if I do:
var s = new net.Server();
s.on('connection', function(socket) {
// Do some lengthy setup process here, blocking execution for a few seconds...
socket.on('data', function(d) { console.log(d); });
});
s.listen(8080);
Meaning not bind to the 'data' event right away, I could lose data? So is this a more robust way to handle incoming connections if you have a lengthy setup required for each one?
var s = new net.Server();
s.on('connection', function(socket) {
socket.pause(); // Not ready for you yet!
// Do some lengthy setup process here, blocking execution for a few seconds...
socket.on('data', function(d) { console.log(d); });
socket.resume(); // Okay, go!
});
s.listen(8080);
Anyone have experience working with listening on raw socket streams to know if this data loss is an issue?
I'm hoping this is an instance where the Net.Socket documentation wasn't updated since v0.10, since the stream documentation has a section that mentions 'data' events started emitting right away in versions prior to 0.10. Were TCP sockets properly updated to not start emitting 'data' packets right away, and the documentation not updated appropriately?
Yes, this is the docs flaw. Here is an example:
var net = require('net')
var server = net.createServer(onConnection)
function onConnection (socket) {
console.log('onConnection')
setTimeout(startReading, 1000)
function startReading () {
socket.on('data', read)
socket.on('end', stopReading)
}
function stopReading () {
socket.removeListener('data', read)
socket.removeListener('end', stopReading)
}
}
function read (data) {
console.log('Received: ' + data.toString('utf8'))
}
server.listen(1234, onListening)
function onListening () {
console.log('onListening')
net.connect(1234, onConnect)
}
function onConnect () {
console.log('onConnect')
this.write('1')
this.write('2')
this.write('3')
this.write('4')
this.write('5')
this.write('6')
}
All the data is received. If you explicitly resume() socket, you will lose it.
Also, if you do your "lengthy" setup in a blocking manner (which you shouldn't) you can't lose any IO as it has no chance to be processed, so no events will be emitted.
I have a Node.js application that contains an http(s) server.
In a specific case, I need to shutdown this server programmatically. What I am currently doing is calling its close() function, but this does not help, as it waits for any kept alive connections to finish first.
So, basically, this shutdowns the server, but only after a minimum wait time of 120 seconds. But I want the server to shutdown immediately - even if this means breaking up with currently handled requests.
What I can not do is a simple
process.exit();
as the server is only part of the application, and the rest of the application should remain running. What I am looking for is conceptually something such as server.destroy(); or something like that.
How could I achieve this?
PS: The keep-alive timeout for connections is usually required, hence it is not a viable option to decrease this time.
The trick is that you need to subscribe to the server's connection event which gives you the socket of the new connection. You need to remember this socket and later on, directly after having called server.close(), destroy that socket using socket.destroy().
Additionally, you need to listen to the socket's close event to remove it from the array if it leaves naturally because its keep-alive timeout does run out.
I have written a small sample application you can use to demonstrate this behavior:
// Create a new server on port 4000
var http = require('http');
var server = http.createServer(function (req, res) {
res.end('Hello world!');
}).listen(4000);
// Maintain a hash of all connected sockets
var sockets = {}, nextSocketId = 0;
server.on('connection', function (socket) {
// Add a newly connected socket
var socketId = nextSocketId++;
sockets[socketId] = socket;
console.log('socket', socketId, 'opened');
// Remove the socket when it closes
socket.on('close', function () {
console.log('socket', socketId, 'closed');
delete sockets[socketId];
});
// Extend socket lifetime for demo purposes
socket.setTimeout(4000);
});
// Count down from 10 seconds
(function countDown (counter) {
console.log(counter);
if (counter > 0)
return setTimeout(countDown, 1000, counter - 1);
// Close the server
server.close(function () { console.log('Server closed!'); });
// Destroy all open sockets
for (var socketId in sockets) {
console.log('socket', socketId, 'destroyed');
sockets[socketId].destroy();
}
})(10);
Basically, what it does is to start a new HTTP server, count from 10 to 0, and close the server after 10 seconds. If no connection has been established, the server shuts down immediately.
If a connection has been established and it is still open, it is destroyed.
If it had already died naturally, only a message is printed out at that point in time.
I found a way to do this without having to keep track of the connections or having to force them closed. I'm not sure how reliable it is across Node versions or if there are any negative consequences to this but it seems to work perfectly fine for what I'm doing. The trick is to emit the "close" event using setImmediate right after calling the close method. This works like so:
server.close(callback);
setImmediate(function(){server.emit('close')});
At least for me, this ends up freeing the port so that I can start a new HTTP(S) service by the time the callback is called (which is pretty much instantly). Existing connections stay open. I'm using this to automatically restart the HTTPS service after renewing a Let's Encrypt certificate.
If you need to keep the process alive after closing the server, then Golo Roden's solution is probably the best.
But if you're closing the server as part of a graceful shutdown of the process, you just need this:
var server = require('http').createServer(myFancyServerLogic);
server.on('connection', function (socket) {socket.unref();});
server.listen(80);
function myFancyServerLogic(req, res) {
req.connection.ref();
res.end('Hello World!', function () {
req.connection.unref();
});
}
Basically, the sockets that your server uses will only keep the process alive while they're actually serving a request. While they're just sitting there idly (because of a Keep-Alive connection), a call to server.close() will close the process, as long as there's nothing else keeping the process alive. If you need to do other things after the server closes, as part of your graceful shutdown, you can hook into process.on('beforeExit', callback) to finish your graceful shutdown procedures.
The https://github.com/isaacs/server-destroy library provides an easy way to destroy() a server with the behavior desired in the question (by tracking opened connections and destroying each of them on server destroy, as described in other answers).
As others have said, the solution is to keep track of all open sockets and close them manually. My node package killable can do this for you. An example (using express, but you can call use killable on any http.server instance):
var killable = require('killable');
var app = require('express')();
var server;
app.route('/', function (req, res, next) {
res.send('Server is going down NOW!');
server.kill(function () {
//the server is down when this is called. That won't take long.
});
});
var server = app.listen(8080);
killable(server);
Yet another nodejs package to perform a shutdown killing connections: http-shutdown, which seems reasonably maintained at the time of writing (Sept. 2016) and worked for me on NodeJS 6.x
From the documentation
Usage
There are currently two ways to use this library. The first is explicit wrapping of the Server object:
// Create the http server
var server = require('http').createServer(function(req, res) {
res.end('Good job!');
});
// Wrap the server object with additional functionality.
// This should be done immediately after server construction, or before you start listening.
// Additional functionailiy needs to be added for http server events to properly shutdown.
server = require('http-shutdown')(server);
// Listen on a port and start taking requests.
server.listen(3000);
// Sometime later... shutdown the server.
server.shutdown(function() {
console.log('Everything is cleanly shutdown.');
});
The second is implicitly adding prototype functionality to the Server object:
// .extend adds a .withShutdown prototype method to the Server object
require('http-shutdown').extend();
var server = require('http').createServer(function(req, res) {
res.end('God job!');
}).withShutdown(); // <-- Easy to chain. Returns the Server object
// Sometime later, shutdown the server.
server.shutdown(function() {
console.log('Everything is cleanly shutdown.');
});
My best guess would be to kill the connections manually (i.e. to forcibly close it's sockets).
Ideally, this should be done by digging into the server's internals and closing it's sockets by hand. Alternatively, one could run a shell-command that does the same (provided the server has proper privileges &c.)
I have answered a variation of "how to terminate a HTTP server" many times on different node.js support channels. Unfortunately, I couldn't recommend any of the existing libraries because they are lacking in one or another way. I have since put together a package that (I believe) is handling all the cases expected of graceful HTTP server termination.
https://github.com/gajus/http-terminator
The main benefit of http-terminator is that:
it does not monkey-patch Node.js API
it immediately destroys all sockets without an attached HTTP request
it allows graceful timeout to sockets with ongoing HTTP requests
it properly handles HTTPS connections
it informs connections using keep-alive that server is shutting down by setting a connection: close header
it does not terminate the Node.js process
Usage:
import http from 'http';
import {
createHttpTerminator,
} from 'http-terminator';
const server = http.createServer();
const httpTerminator = createHttpTerminator({
server,
});
await httpTerminator.terminate();
const Koa = require('koa')
const app = new Koa()
let keepAlive = true
app.use(async (ctx) => {
let url = ctx.request.url
// destroy socket
if (keepAlive === false) {
ctx.response.set('Connection', 'close')
}
switch (url) {
case '/restart':
ctx.body = 'success'
process.send('restart')
break;
default:
ctx.body = 'world-----' + Date.now()
}
})
const server = app.listen(9011)
process.on('message', (data, sendHandle) => {
if (data == 'stop') {
keepAlive = false
server.close();
}
})
process.exit(code); // code 0 for success and 1 for fail
I'm making simple online game which based on Web.
the game uses Socket.io for netwoking each other.
but I encountered the problem.
think about following situation .
I ran Socket.io server.
one player making the room , and other player join the room.
they played game some time ..
but one player so angry and close the game tab.
in this situation , how can I get the event which one client have been closed the browser in server-side ?
according to googling , peoples say like this : "use browser-close event like onBeforeUnload"
but I know that All browser don't support onBeforeUnload event. so i want solution about
checking the client disconnection event in SERVER SIDE.
in Socket.io ( nodeJS ) server-side console , when client's connection closed , the console say like following :
debug - discarding transport
My nodeJS version is 0.4.10 and Socket.io version is 0.8.7. and both are running on Linux.
Anyone can help please ?
shortend codes are here :
var io = require ( "socket.io" ).listen ( 3335 );
io.sockets.on ( "connection" , function ( socket )
{
socket.on ( "req_create_room" , function ( roomId )
{
var socketInstance = io
.of ( "/" + roomId )
.on ( "connection" , function ( sock )
{
sock.on ( "disconnect" , function ()
{
// i want this socket data always displayed...
// but first-connected-client doesn't fire this event ..
console.log ( sock );
}
});
});
});
Update: I created a blog post for this solution. Any feedback is welcome!
I recommend using the 'sync disconnect on unload' option for Socket IO. I was having similar problems, and this really helped me out.
On the client:
var socket = io.connect(<your_url>, {
'sync disconnect on unload': true });
No need to wire in any unload or beforeunload events. Tried this out in several browsers, and its worked perfectly so far.
There's an event disconnect which fires whenever a socket.io connection dies (note that you need this, because you may still have a wep page open, but what if your internet connection dies?). Try this:
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
socket.on('disconnect', function () {
io.sockets.emit('user disconnected');
});
});
at your server. Taken from Socket.IO website.
//EDIT
So I looked at your code and did some tests at my place. I obtained the very same results as you and here's my explanation. You are trying to make Socket.IO very dynamic by dynamically forcing it to listen to different urls (which are added at runtime). But when the first connection is made, at that moment the server does not listen to the other url. It seems that exactly at that point (when connection is accepted) the disconnect handler is set for the first connection and it is not updated later (note that Socket.IO does not create new connections when you call io.connect many times at the client side). All in all this seems to be a bug! Or perhaps there is some fancy explanation why this behaviour should be as it is but I do not know it.
Let me tell you some other things. First of all this dynamical creation of listeners does not seem to be a good way. In my opinion you should do the following: store the existing rooms and use one url for all of them. Hold the ID of a room and when you emit for example message event from client add the ID of a room to the data and handle this with one message handler at the server. I think you get the idea. Push the dynamic part into the data, not urls. But I might be wrong, at least that's my opinion.
Another thing is that the code you wrote seems to be bad. Note that running .on('connection', handler) many times will make it fire many times. Handlers stack one onto another, they do not replace each other. So this is how I would implement this:
var io = require("socket.io").listen(app);
var roomIds = [];
function update_listeners(id) {
io.of("/"+id).on("connection", function(socket) {
console.log("I'm in room " + id);
socket.on("disconnect", function(s) {
console.log("Disconnected from " + roomId);
});
});
}
var test = io.sockets.on("connection", function(socket) {
console.log("I'm in global connection handler");
socket.on("req_create_room", function(data) {
if (roomIds.indexOf(data.roomId) == -1 ) {
roomIds.push(data.roomId);
update_listeners(data.roomId);
}
test.emit("room_created", {ok:true});
});
socket.on("disconnect", function(s) {
console.log("Disconnected from global handler");
});
});
Keep in mind that the problem with creating connections before the listeners are defined will still occure.