How does one properly shutdown socket.io / websocket-client? - node.js

I'm trying to create a test using LearnBoost's socket.io and the node-websocket-client. Communication between the client and server work great. After all communication is done, I close both the client and the server. Yet the program hangs, waiting on some unknown callback. Two questions:
What is the following program waiting for?
Is there a tool for diagnosing outstanding callbacks in node programs?
var connect = require('connect'),
io = require('socket.io'),
WebSocket = require('websocket-client').WebSocket;
var port = 7111;
var server = connect.createServer();
var socket = io.listen(server);
socket.on('connection', function(client) {
client.send('Welcome!');
client.on('message', function(message) {
console.log(message);
});
client.on('disconnect', function() {
console.log('closing');
server.close();
});
});
server.listen(port, function() {
var ws = new WebSocket('ws://localhost:' + port + '/socket.io/websocket');
ws.onmessage = function(message) {
console.log(message.data);
};
setTimeout(function() {
ws.send('~m~3~m~Yo!');
ws.close();
}, 10);
});
EDIT: changed the variable name of the WebSocket to ws to avoid confusion

var socket = io.listen(server);
You've created a socket on a port. You've never closed it.
socket.server.close() closes your (socket.io) socket.
When in doubt read the socket.io github examples
socket.server === server It's the server you pass in, in the liste statement so it's closed. I'm not sure what it's waiting for.

Below a way to shutdown all the connections and be able to run multiple expresso tests (using socket.io and socket.io-client).
The solution is tricky and buggy but works on 0.8.5. The main problem is regarding the library to use websockets (node-websocket-client).
Currently, on socket.io, the OS contributors have patched the websocket client. So, we must do the same on our socket.io-client npm package to be able to use finishClose method on the socket client side. Socket.io-client uses the websocket library as npm package, so you must find the file (websocket.js) and substitute it with the same on socket.io.
Afterwards, you could use finishClose method to ensure the connections are closed and with some custom server/client socket settings, the tests will run correctly.
var io = require("socket.io").listen(port);
io.set('close timeout', .2);
io.set('client store expiration', .2);
var client = require("socket.io-client").connect( "http://localhost", { port: port , 'reconnect': false, 'force new connection': true});
client.on('connect', function() {
client.disconnect();
});
client.on('disconnect', function() {
client.socket.transport.websocket.finishClose();
io.server.close();
});
io.server.on('close', function() {
setTimeout( function() {
done();
}, 500);
});
Hope, somebody can help.

The program is waiting because socket.io (server) is still listening for incoming connections. I don't know of any way to stop listening.

Related

Nodejs request proxy stream(mjpeg) connection never ends

(unnecessary backstory)
I have a nodejs server with expressjs framework that's proxy streaming a webcam feed. The reason I need this is because the mjpg stream must come from this server due to complex CORS issues.
//proxy from webcam server to avoid CORS complaining
app.get('/stream1',function(req,res){
var url="http://camera.nton.lviv.ua/mjpg/video.mjpg"
request(url).pipe(res);
});
question :
The issue is simple. request(url).pipe(res) never closes, because the source is mjpeg which literally never ends. I need to find a way to force close this pipe when the client(browser; the destination) is no longer available - as in, closes the window.
The other answers did not work for me.
This line var pipe=request(url).pipe(res);
returns the pipe instead of the request object. So I needed to break the line up.
The request object is needed to abort. Calling the .end() didn't work either, but the .abort() did the trick. It took me hours to find the answer that worked for me, so I thought I would share.
app.get('/cam/frontdoor',function(req,res){
var request_options = {
auth: {
user: '',
pass: ''},
url: 'http:/xx.xx.xx.xx/mjpg/video.mjpg',
};
var req_pipe = request(request_options);
req_pipe.pipe(res);
req_pipe.on('error', function(e){
console.log(e)
});
//client quit normally
req.on('end', function(){
console.log('end');
req_pipe.abort();
});
//client quit unexpectedly
req.on('close', function(){
console.log('close');
req_pipe.abort()
})
})
Use socket.io to monitor the remote connection
// install it on your project
npm install socket.io
// require it on server side
var socket = require('socket.io');
// listen for sockets from your server
var mysocks = socket.listen(myexpressappvar);
// keep collection of sockets for use if needed
// its good practice
var connectedSockets = [];
// add event handelers on server side
mysocks.sockets.on("connection", function(socket){
// add socket to our collection
connectedSockets.push(socket);
// you will need to bind their stream id here.
exe.....
// listen for disconnected
socket.on("disconnect", function(){
// remove socket from collection
connections.splice(connections.indexOf(socket), 1);
// destory stream here
exe...
});
});
// last thing, is add socket.io to the client side.
<script src="/socket.io/socket.io.js"></script>
// then call connect from client side js file
var socket = io.connect();
I have found out a simpler way. Add a event listener for client connection closing, and force close the pipe when it happens.
app.get('/stream1',function(req,res){
var url="http://camera.nton.lviv.ua/mjpg/video.mjpg"
var pipe=request(url).pipe(res);
pipe.on('error', function(){
console.log('error handling is needed because pipe will break once pipe.end() is called')
}
//client quit normally
req.on('end', function(){
pipe.end();
}
//client quit unexpectedly
req.on('close', function(){
pipe.end();
}
});

Node js - Socket.io-client is not connecting to socket.io server

I am trying to connect to a socket.io-client using the following code:
Server:
// Load requirements
var http = require('http'),
io = require('socket.io');
// Create server & socket
var server = http.createServer(function(req, res){
// Send HTML headers and message
res.writeHead(404, {'Content-Type': 'text/html'});
res.end('<h1>Aw, snap! 404</h1>');
});
server.listen(8080);
io = io.listen(server);
// Add a connect listener
io.sockets.on('connection', function(socket) {
console.log('Client connected.');
// Disconnect listener
socket.on('disconnect', function() {
console.log('Client disconnected.');
});
});
Client:
console.log('1');
// Connect to server
var io = require('socket.io-client')
var socket = io.connect('localhost:8080', {reconnect: true});
console.log('2');
// Add a connect listener
socket.on('connect', function(socket) {
console.log('Connected!');
});
console.log('3');
I don't get the Connected console log or Client Connected console log and I don't know why! The code sample is taken from another question posted: Link and I don't see any solution to the problem...
Use the same version of socket io client and server. It will work perfectly.
Also you need to add protocol with path.
change
var socket = io.connect('localhost:8080', {reconnect: true});
to
var socket = io.connect('http://localhost:8080', {reconnect: true});
Assuming you are using a socket.io version greater than 1.0, on the server, change this:
// Add a connect listener
io.sockets.on('connection', function(socket) {
console.log('Client connected.');
// Disconnect listener
socket.on('disconnect', function() {
console.log('Client disconnected.');
});
});
to this:
// Add a connect listener
io.on('connection', function(socket) {
console.log('Client connected.');
// Disconnect listener
socket.on('disconnect', function() {
console.log('Client disconnected.');
});
});
See the socket.io documentation reference here.
You don't want to be listening for this event only on already connected sockets. You want to listen for this event on any socket, even a newly created one.
Also, be very careful when reading socket.io code in random places on the internet. Some things changed significantly from v0.9 to v1.0 (I don't know if this was one of those things or not). You should generally always start with the socket.io documentation site first since that will always represent the latest version. Then, if looking at other internet references, make sure you only use articles that are later than mid-2014. If you don't know the vintage of an article, it's best not to rely on it without corroboration from a more recent article.
you can use localhost. It works for me as well. You must use your ip address and port that works for you

Why is my connection of a NodeJS server (Sails) to another NodeJS Server (Express4) using Socket.IO failing?

I am trying to connect my Node.JS (written using Sails.JS) app to another Node.JS server (Express4 / Socket.io) using socket.io-client.
My Sails Service app/services/Watcher.js looks like
var client = require('../../node_modules/sails/node_modules/socket.io/node_modules/socket.io-client');
// callback of the form function(socket)
exports.connect = function(callback) {
sails.log.debug("will connect socket to", sails.config.watcher.uri, "with Socket.io-client version", client.version);
var socket = client.connect(sails.config.watcher.uri);
socket.on('connect', function(){
sails.log.debug("connected");
socket.on('disconnect', function(){
sails.log.debug("Disconnected");
});
socket.on('error', function(err){
sails.log.debug("Could not connect", err);
});
callback(socket);
});
};
This is invoked from config/bootstrap.js as follows:
Watcher.connect(function(socket){
sails.log.debug("Connected watcher to relay with socket", socket);
});
On the Express side my server relay.js is as simple as:
var app = require('express')(),
http = require('http').Server(app),
io = require('socket.io').listen(http),
port = process.env.RELAY_PORT || 8000;
app.get('/', function(req, res) {
var response = {message: "some response"}; // to be implemented.
res.json(response);
});
http.listen(port, function () {
console.log("Relay listening on port " + port);
});
io.sockets.on('connection', function (socket) {
console.log("Connection opened", socket);
socket.on('disconnect', function () {
console.log("Socket disconnected");
});
});
When I run node relay it dutifully reports
Relay listening on port 8000
When I sails lift my other server it dutifully reports
will connect socket to http://localhost:8000 with Socket.io-client version 0.9.16
But I never see an actual connection.
If I point a browser at localhost:8000 I get the {"message":"some response"} JSON response I expect.
Why isn't my relay server accepting a connection from my socker.io-client app?
The issue here is probably that you're trying to re-use the
socket.io-client from inside of Sails. In general, if you're require()-ing dependencies of Sails directly in your project, you're heading in the wrong direction. In this case, socket.io-client caches configurations and connections, so your require isn't getting a fresh copy.
Instead, do
npm install socket.io-client#~0.9.16 --save
in your project and require with
var client = require('socket.io-client');
that'll give you a fresh copy of the socket client to work with, and avoid any conflicts with the Sails core's version.

Force close all connections in a node.js http server

I have an http server created using:
var server = http.createServer()
I want to shut down the server. Presumably I'd do this by calling:
server.close()
However, this only prevents the server from receiving any new http connections. It does not close any that are still open. http.close() takes a callback, and that callback does not get executed until all open connections have actually disconnected. Is there a way to force close everything?
The root of the problem for me is that I have Mocha tests that start up an http server in their setup (beforeEach()) and then shut it down in their teardown (afterEach()). But since just calling server.close() won't fully shut things down, the subsequent http.createServer() often results in an EADDRINUSE error. Waiting for close() to finish also isn't an option, since open connections might take a really long time to time out.
I need some way to force-close connections. I'm able to do this client-side, but forcing all of my test connections to close, but I'd rather do it server-side, i.e. to just tell the http server to hard-close all sockets.
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
You also have the chance to run the server in a child process and exit that process when you need.
For reference for others who stumble accross this question, the https://github.com/isaacs/server-destroy library provides an easy way to destroy() a server (using the approach described by Ege).
I usually use something similar to this:
var express = require('express');
var server = express();
/* a dummy route */
server.get('/', function (req, res) {
res.send('Hello World!');
});
/* handle SIGTERM and SIGINT (ctrl-c) nicely */
process.once('SIGTERM', end);
process.once('SIGINT', end);
var listener = server.listen(8000, function(err) {
if (err) throw err;
var host = listener.address().address;
var port = listener.address().port;
console.log('Server listening at http://%s:%s', host, port);
});
var lastSocketKey = 0;
var socketMap = {};
listener.on('connection', function(socket) {
/* generate a new, unique socket-key */
var socketKey = ++lastSocketKey;
/* add socket when it is connected */
socketMap[socketKey] = socket;
socket.on('close', function() {
/* remove socket when it is closed */
delete socketMap[socketKey];
});
});
function end() {
/* loop through all sockets and destroy them */
Object.keys(socketMap).forEach(function(socketKey){
socketMap[socketKey].destroy();
});
/* after all the sockets are destroyed, we may close the server! */
listener.close(function(err){
if(err) throw err();
console.log('Server stopped');
/* exit gracefully */
process.exit(0);
});
}
it's like Ege Özcan says, simply collect the sockets on the connection event, and when closing the server, destroy them.
I've rewriten original answers using modern JS:
const server1 = http.createServer(/*....*/);
const server1Sockets = new Set();
server1.on("connection", socket => {
server1Sockets.add(socket);
socket.on("close", () => {
server1Sockets.delete(socket);
});
});
function destroySockets(sockets) {
for (const socket of sockets.values()) {
socket.destroy();
}
}
destroySockets(server1Sockets);
My approach comes from this one and it basically does what #Ege Özcan said.
The only addition is that I set a route to switch off my server because node wasn't getting the signals from my terminal ('SIGTERM' and 'SIGINT').
Well, node was getting the signals from my terminal when doing node whatever.js but when delegating that task to a script (like the 'start' script in package.json --> npm start) it failed to be switched off by Ctrl+C, so this approach worked for me.
Please note I am under Cygwin and for me killing a server before this meant to close the terminal and reopen it again.
Also note that I am using express for the routing stuff.
var http=require('http');
var express= require('express');
var app= express();
app.get('/', function (req, res) {
res.send('I am alive but if you want to kill me just go to /exit');
});
app.get('/exit', killserver);
var server =http.createServer(app).listen(3000, function(){
console.log('Express server listening on port 3000');
/*console.log(process);*/
});
// 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);
});
// close the server and destroy all the open sockets
function killserver() {
console.log("U killed me but I'll take my revenge soon!!");
// 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();
}
};
There is now a closeAllConnections() method in v18.2.0

Socket.io - Close Server

I have a socket.io server in my app, listening on port 5759.
At some point in my code I need to shutdown the server SO IT IS NOT LISTENING ANYMORE.
How Can I accomplish this?
Socket.io is not listening on an http server.
You have a server :
var io = require('socket.io').listen(8000);
io.sockets.on('connection', function(socket) {
socket.emit('socket_is_connected','You are connected!');
});
To stop recieving incoming connections
io.server.close();
NOTE: This will not close existing connections, which will wait for timeout before they are closed. To close them immediately , first make a list of connected sockets
var socketlist = [];
io.sockets.on('connection', function(socket) {
socketlist.push(socket);
socket.emit('socket_is_connected','You are connected!');
socket.on('close', function () {
console.log('socket closed');
socketlist.splice(socketlist.indexOf(socket), 1);
});
});
Then close all existing connections
socketlist.forEach(function(socket) {
socket.destroy();
});
Logic picked up from here : How do I shutdown a Node.js http(s) server immediately?
This api has changed again in socket.io v1.1.x
it is now:
io.close()
The API has changed. To stop receiving incoming connections you should run:
io.httpServer.close();

Resources