node net socket connection timeout - node.js

I noticed, that my node server's net.createConnection() has a very long timeout before firing an error on some occasions (seems to be a particular problem with ports...)
i tried to connect to somedomain:9000 (listening, connecting and working as expected)
and to somedomain:1234 (same domain, different port, waiting around 2 minutes until "connect ETIMEDOUT")
When i connect to non-existent domains, i get an error right away, but not if i connect to unreachable ports on reachable hosts.
i need to determine if a machine is reachable in <1sec..
how do i handle this? Must be some way to notice an unreachable port in under 2minutes?
at least some kind of timeout that just sets the address as unreachable after a set amout of time?
Thanks
UPDATE: current Connection code:
this.openConnection = function() {
try {
console.log("[INFO] connecting to " + device.ip + ":" + device.port);
device.clientSocket = new net.createConnection(this.port,this.ip)
.on('connect', device.connected)
.on('data', device.inputReceived)
.on('error', function(err) {
if (err.code == "ENOTFOUND") {
console.log("[ERROR] No device found at this address!");
device.clientSocket.destroy();
return;
}
if (err.code == "ECONNREFUSED") {
console.log("[ERROR] Connection refused! Please check the IP.");
device.clientSocket.destroy();
return;
}
console.log("[CONNECTION] Unexpected error! " + err.message + " RESTARTING SERVER");
process.exit(1);
})
.on('disconnect', function() {
console.log("[CONNECTION] disconnected!");
});
} catch(err) {
console.log("[CONNECTION] connection failed! " + err);
}
};

When you connect, you can just set your own timer for whatever timeout you want and if the connect has not succeeded when that timer fires, then it did not succeed as quickly as you want.
This could be encapsulated in a single function with a single callback or returning a promise.
Based on your code, here's a shot at adding a timeout to it (untested code):
this.openConnection = function(timeout) {
var timer;
timeout = timeout || 2000;
try {
console.log("[INFO] connecting to " + device.ip + ":" + device.port);
device.clientSocket = new net.createConnection(this.port,this.ip)
.on('connect', function() {
clearTimeout(timer);
device.connected();
})
.on('data', function() {
clearTimeout(timer);
device.inputReceived();
})
.on('error', function(err) {
clearTimeout(timer);
if (err.code == "ENOTFOUND") {
console.log("[ERROR] No device found at this address!");
device.clientSocket.destroy();
return;
}
if (err.code == "ECONNREFUSED") {
console.log("[ERROR] Connection refused! Please check the IP.");
device.clientSocket.destroy();
return;
}
console.log("[CONNECTION] Unexpected error! " + err.message + " RESTARTING SERVER");
process.exit(1);
})
.on('disconnect', function() {
console.log("[CONNECTION] disconnected!");
});
timer = setTimeout(function() {
console.log("[ERROR] Attempt at connection exceeded timeout value");
device.clientSocket.end();
}, timeout);
} catch(err) {
console.log("[CONNECTION] connection failed! " + err);
}
};

Related

Emergency! Error: This socket has been ended by the other party

I revised appium source code, add my code, when i connect to the port that is forwarded to device and send command to port, it comes out:
Error: This socket has been ended by the other party
and my code is like this:
return await new Promise((resolve, reject) => {
try {
this.socketClient = net.connect(this.webSocket);
// Windows: the socket errors out when ADB restarts. Let's catch it to avoid crashing.
this.socketClient.on('error', (err) => {
if (!this.ignoreUnexpectedShutdown) {
//throw new Error(`Android bootstrap socket crashed: ${err}`);
log.debug('//////////////////////////////////')
log.debug(err)
log.debug('//////////////////////////////////')
throw new Error(`Android testbundle socket crashed: ${err}`)
}
});
this.socketClient.once('connect', () => {
log.info("Android bundle socket is now connected");
resolve();
});
} catch (err) {
reject(err);
}
})
after that, I use this.socketClient to send command like this:
async sendCommand(type, extra = {}) {
if (!this.socketClient) {
log.debug('==========socket closed========')
throw new Error('Socket connection closed unexpectedly');
}
return await new B((resolve, reject) => {
let cmd = Object.assign({cmd: type}, extra);
let cmdJson = `${JSON.stringify(cmd)}\n`;
log.debug(`Sending command to android testbundle: ${_.trunc(cmdJson, 1000).trim()}`);
this.socketClient.write(cmdJson);
this.socketClient.setEncoding('utf8');
let streamData = '';
this.socketClient.on('data', (data) => {
try {
streamData = JSON.parse(streamData + data);
// we successfully parsed JSON so we've got all the data,
// remove the socket listener and evaluate
this.socketClient.removeAllListeners('data');
if (streamData.status === 0) {
resolve(streamData.value);
}
log.debug("Received command result from bundle:" + JSON.stringify(streamData));
reject(errorFromCode(streamData.status));
} catch (ign) {
log.debug("Stream still not complete, waiting");
streamData += data;
}
})
})
}
But, I always get the error:
[debug] [bundle] //////////////////////////////////
[debug] [bundle] Error: This socket has been ended by the other party
at Socket.writeAfterFIN [as write] (net.js:291:12)
at ..\../lib/bundle.js:160:31
Anyone can help me...

amqp.node won't detect a connection drop

We have a node.js script running a socket.io server whose clients consume messages from a RabbitMQ queue. We've recently migrated to Amazon AWS and RabbitMQ is now a cluster of two machines (redundant instances). The AMQP connection is lost from time to time (it is a limitation that arrives from a high availability environment with redundant VMs and we have to cope with it) and if an attempt to reconnect is made, the DNS chooses which instance to connect to (it is a cluster with data replication so it doesn't matter which instance to connect to).
The problem is that the attempt to reconnect is never made; after a while, when the connection is lost, amqp.node apparently fails to notice that the connection has been lost. Also, the consumers stop receiving messages and the socket.io server simply stops accepting new connections.
We have a 55 seconds heartbeat timeout (not to be confused with the socket.io heartbeat timeout) set at the RabbitMQ URL and are checking for 'error' and 'close' events with amqp.node's callback API but they are apparently never issued. The queues expect the consumed messages to be ack'ed. We want the node script to detect a lost connection and finish itself, so the environment will automatically start a new process and establish a connection again.
Here is the code, maybe we are doing something wrong with the amqp.node callback API or something else.
var express = require('express');
app = express();
var http = require('http');
var serverio = http.createServer(app);
var io = require('socket.io').listen(serverio, { log: false });
var socket;
var allcli = [];
var red, blue, green, magenta, reset;
red = '\033[31m';
blue = '\033[34m';
green = '\033[32m';
magenta = '\033[35m';
orange = '\033[43m';
reset = '\033[0m';
var queue = 'ha.atualizacao_mobile';
var urlRabbit = 'amqp://login:password#host?heartbeat=55' // Amazon
var amqp = require('amqplib/callback_api');
var debug = true;
console.log("Original Socket.IO heartbeat interval: " + io.get('heartbeat interval') + " seconds.");
io.set('heartbeat interval', 10 * 60);
console.log("Hearbeat interval changed to " + io.get('heartbeat interval') + " seconds to reduce battery consumption in the mobile clients.");
console.log("Original Socket.IO heartbeat timeout: " + io.get('heartbeat timeout') + " seconds.");
io.set('heartbeat timeout', 11 * 60);
console.log("Heartbeat timeout set to " + io.get('heartbeat timeout') + " seconds.");
io.sockets.on('connection', function(socket){
socket.on('error', function (exc) {
console.log(orange+"Ignoring exception: " + exc + reset);
});
socket.on('send-indice', function (data) {
// Some business logic
});
socket.on('disconnect', function () {
// Some business logic
});
});
function updatecli(data){
// Some business logic
}
amqp.connect(urlRabbit, null, function(err, conn) {
if (err !== null) {
return console.log("Error creating connection: " + err);
}
conn.on('error', function(err) {
console.log("Generated event 'error': " + err);
});
conn.on('close', function() {
console.log("Connection closed.");
process.exit();
});
processRabbitConnection(conn, function() {
conn.close();
});
});
function processRabbitConnection(conn, finalize) {
conn.createChannel(function(err, channel) {
if (err != null) {
console.log("Error creating channel: " + err);
return finalize();
}
channel.assertQueue(queue, null, function(err, ok) {
if (err !== null) {
console.log("Error asserting queue " + queue + ": " + err);
return finalize();
}
channel.consume(queue, function (msg) {
if (msg !== null) {
try {
var dataObj = JSON.parse(msg.content);
if (debug == true) {
//console.log(dataObj);
}
updatecli(dataObj);
} catch(err) {
console.log("Error in JSON: " + err);
}
channel.ack(msg);
}
}, null, function(err, ok) {
if (err !== null) {
console.log("Error consuming message: " + err);
return finalize();
}
});
});
});
}
serverio.listen(9128, function () {
console.log('Server: Socket IO Online - Port: 9128 - ' + new Date());
});
Apparently the issue has been solved. The near 60 seconds heartbeat was the issue. It conflicts with the RabbitMQ load balancer which checks every 1 minute or so whether data has passed through the connection or not (if no data has passed, it breaks the connection). The AMQP connection stops receiving messages and the library apparently doesn't react to that. A lower heartbeat (e.g. 30 seconds) is necessary in order to avoid this situation.

AMQP Connection Closed certain time interval with node js

I tried out last 3 days with the below issue. Kindly help me to resolve the issue,
>Error: Unexpected close
at succeed (/usr/local/lib/node_modules/amqplib/lib/connection.js:259:13)
at onOpenOk (/usr/local/lib/node_modules/amqplib/lib/connection.js:241:5)
at /usr/local/lib/node_modules/amqplib/lib/connection.js:160:32
at /usr/local/lib/node_modules/amqplib/lib/connection.js:154:12
at Socket.recv (/usr/local/lib/node_modules/amqplib/lib/connection.js:480:12)
at Socket.g (events.js:180:16)
at Socket.EventEmitter.emit (events.js:92:17)
at emitReadable_ (_stream_readable.js:407:10)
at emitReadable (_stream_readable.js:403:5)
at readableAddChunk (_stream_readable.js:165:9)
I am using amqplib + node js. Whenever i started the server i got the above error with a time interval. Maximum it will occurs at 5 mins interval.
amqplib = amqplib.connect('amqp://'+rabit_host).then(function(conn)
{
amqpconnection = conn;
});
io.sockets.on('connection', function(client)
{
client.on('receivemsg', function(arg)
{
amqpconnection.createConfirmChannel().then(function(channelObjSuccess)
{
channelObjSuccess.assertQueue(queue_name,{durable:false,autoDelete:true});
client.assignObj = channelObjSuccess;
channelObjSuccess.consume(queue_name, function(msg)
{
var encodemsg = msg.content.toString();
var json_msg = JSON.parse(encodemsg);
client.emit('chatrecive',json_msg);
}).then(function(){
console.log("Receive Consiuume Close");
});
});
});
client.on('loginentry', function(arg)
{
amqpconnection.createConfirmChannel().then(function(channelObjSuccess) {
channelObjSuccess.assertQueue(queue_name,{durable:false,autoDelete:true});
});
});
client.on('sendmsg', function(arg)
{
var payload_stringify = JSON.stringify(arg);
amqpconnection.createConfirmChannel().then(function(channelObjSuccess) {
channelObjSuccess.assertQueue(queue_name,{durable:false,autoDelete:true});
channelObjSuccess.sendToQueue(queue_name, new Buffer(payload_stringify), {},
function(err, ok)
{
if (err !== null)
console.log('Message Send Failure! ');
else
{
channelObjSuccess.close();
}
});
});
});
client.on('disconnect', function()
{
try {
console.log("AMPQ Connection Closed - Disconnect");
if(typeof(client.assignObj)!=undefined)
{
client.assignObj.close();
}
}
catch (alreadyClosed) {
console.log("RabbitMQ Connection Already Closed " + alreadyClosed.stackAtStateChange);
}
});
});
server.listen(port);
I think i found the answer, might be the issue is with the heartbeat as option while connecting to the AMQP.
Eg:
url = "amqp://turtle.rmq.cloudamqp.com/bqftjxzn?heartbeat=45";

Node restart UDP server

I'm creating a udp server for receiving a message and sending a response quickly.
I find myself, however, to have a problem: the server can not close the connection to the client once it has been answered.
So, to avoid saturating the server, I thought of a way to restart the server udp (a sort of re-bind), once a counter has reached a certain number, but I can not find a solution to close the server udp and restart it.
Is there a way to do this?
Here a code snippet example of what I have at the moment:
/** Udp server */
var reset = function() {
if(count > 3) {
server.close();
count = 0;
console.log('reset');
server.bind('4002');
}
return true;
};
var count = 0;
var server = require("dgram").createSocket("udp4");
server.on("message", function(message, requestInfo) {
count++;
message = message.toString();
if(message == null || message === '') {
reset();
return;
}
// do something with the received message and sends the reply ..
var response = new Buffer(result.toString());
server.send(response, 0, response.length, requestInfo.port, requestInfo.address);
reset();
}
);
server.on("error", function(error) {
console.log(error);
server.close();
});
server.on("listening", function() {
var address = server.address();
console.log("server listening " + address.address + ":" + address.port);
});
server.bind('4002');
This is the stack trace:
events.js:72
throw er; // Unhandled 'error' event
^
Error: Not running
at Socket._healthCheck (dgram.js:420:11)
at Socket.bind (dgram.js:160:8)
...

terminate socket connection on refresh or reload nodejs

I have a node.js application subscribed to a channel listening for an event. When it hears that event it publishes another event to a new channel. I have been using MubSub for my publish/subscriptions. My issue is that no matter what I do every time the page is reloaded there are two listeners and two publishes, then 3,4,5,6 etc..
I dont understand why they do not get disconnected?
if (curURL == '/handwash') {
db2.handwash.find({
"event": "handwash"
}).sort({
"message.time": -1
}).limit(10).forEach(function(error, x) {
if (error || !x) {
console.log("error getting items");
} else {
socket.emit('archive', x.message);
}
});
channel2.subscribe('dealer out', function(message) {
console.log("new dealer event " + message);
db.events.find({
"event": "wash"
}).sort({
"message.time": -1
}).limit(1).forEach(function(error, x) {
if (error || !x) {
console.log("error getting items");
} else {
console.log("found last handwash..." + x.message.type + "....publishing");
var message = ({
'tableid': 123,
'time': Date(),
'type': x.message.type,
})
channel.publish('handwash', message );
socket.emit('message', message);
}
});
//socket.emit('message', message);
});
socket.on("disconnect", function(s) {
console.log("Disconnected from global handler");
});
}
The problem was that I was using MubSub for subscription and publishing messages. Fixed it by creating a varaible for my connection and then closing it with a unsubscribe();
var subscription = channelEvents.subscribe('pocket', function(message) {}
subscription.unsubscribe();

Resources