Using setInterval with socket.write - node.js

This is the code that works but it writes the data just once:
var net = require('net');
var PORT = 3000;
var client = new net.Socket();
client.connect(PORT, function(){
client.write('{printing}');
})
I am looking to write the same thing every few seconds. Wrote the below code but it doesn't seem to work:
client.connect(PORT, function(){
setInterval(function(){
client.write('{ printing }');
},10000);
})
Following is the error that I keep getting:
node:events:355
throw er; // Unhandled 'error' event
^
Error: write EPIPE
at afterWriteDispatched (node:internal/stream_base_commons:160:15)
at writeGeneric (node:internal/stream_base_commons:151:3)
at Socket._writeGeneric (node:net:773:11)
at Socket._write (node:net:785:8)
at writeOrBuffer (node:internal/streams/writable:395:12)
at Socket.Writable.write (node:internal/streams/writable:340:10)
at Timeout._onTimeout (/app/src/index.js:135:14)
at listOnTimeout (node:internal/timers:557:17)
at processTimers (node:internal/timers:500:7)
Emitted 'error' event on Socket instance at:
at emitErrorNT (node:internal/streams/destroy:188:8)
at emitErrorCloseNT (node:internal/streams/destroy:153:3)
at processTicksAndRejections (node:internal/process/task_queues:81:21) {
errno: -32,
code: 'EPIPE',
syscall: 'write'
}
[nodemon] app crashed - waiting for file changes before starting..
This is how I fixed it:
client.connect(PORT, function(){
client.write('printing')
})
//adding drain if the buffer gets full
client.on('drain',()=>{
console.log("draining the buffer")
setTimeout(() => {
client.write('printing')
})
//reading the response recieved : ("ok")
client.on('data', (data) => {})
//in case of an error, closing the connection
client.on('error',err => {}).on('close',() => {
setTimeout(() => {
client.connect(PORT, function(){
client.write('printing')
})
},40000)
})

In this context, the EPIPE error probably means that you're trying to write to a socket that has been closed. Since the setInterval() example you show keeps going forever, that probably means that the socket you originally connected gets closed at some point, but your setInterval() is still firing and trying to write to it.
You don't show the overall context of what you're trying to accomplish here to know exactly what to suggest, but at a minimum, you need to call clearInterval() to stop the timer whenever the socket it's trying to write to gets closed, either on purpose or because of error.
Here's an example for how you could debug if this is what is happening to you:
const net = require('net');
const PORT = 3000;
const client = new net.Socket();
let timer;
function disableTimer() {
if (timer) {
clearInterval(timer);
timer = null;
}
}
client.on('error', err => {
console.log("socket error", err);
disableTimer();
}).on('close', () => {
console.log("socket closed");
disableTimer();
});
client.connect(PORT, function(){
timer = setInterval(function(){
client.write('{ printing }');
},10000);
});

Related

Node.js redis#4 Upgrade: SocketClosedUnexpectedlyError: Socket closed unexpectedly

I've got some legacy code that I'm upgrading from version 3 of the Node.js redis library to version 4 of the Node.js redis library. The basic shape of the code looks like this
var redis = require('redis')
var client = redis.createClient({
port: '6379',
host: process.env.REDIS_HOST,
legacyMode: true
})
client.connect()
client.flushall(function (err, reply) {
client.hkeys('hash key', function (err, replies) {
console.log("key set done")
client.quit()
})
})
console.log("main done")
When I run this code with redis#4.3.1, I get the following error, and node.js exits with a non-zero status code
main done
key set done
events.js:292
throw er; // Unhandled 'error' event
^
SocketClosedUnexpectedlyError: Socket closed unexpectedly
at Socket.<anonymous> (/Users/astorm/Documents/redis4/node_modules/#redis/client/dist/lib/client/socket.js:182:118)
at Object.onceWrapper (events.js:422:26)
at Socket.emit (events.js:315:20)
at TCP.<anonymous> (net.js:673:12)
Emitted 'error' event on Commander instance at:
at RedisSocket.<anonymous> (/Users/astorm/Documents/redis4/node_modules/#redis/client/dist/lib/client/index.js:350:14)
at RedisSocket.emit (events.js:315:20)
at RedisSocket._RedisSocket_onSocketError (/Users/astorm/Documents/redis4/node_modules/#redis/client/dist/lib/client/socket.js:205:10)
at Socket.<anonymous> (/Users/astorm/Documents/redis4/node_modules/#redis/client/dist/lib/client/socket.js:182:107)
at Object.onceWrapper (events.js:422:26)
at Socket.emit (events.js:315:20)
at TCP.<anonymous> (net.js:673:12)
While in redis#3.1.2 it runs (minus the client.connect()) without issue.
I've been able to work around this by replacing client.quit() with client.disconnect(), but the actual code is a little more complex than the above example and I'd rather use the graceful shutdown of client.quit than the harsher "SHUT IT DOWN NOW" of client.disconnect().
Does anyone know what the issue here might be? Why is redis#4 failing with a SocketClosedUnexpectedlyError: Socket closed unexpectedly error.
What I found so far is that after a while (keepAlive default is 5 minutes) without any requests the Redis client closes and throws an error event, but if you don't handle this event it will crash your application.
My solution for that was:
/* eslint-disable no-inline-comments */
import type { RedisClientType } from 'redis'
import { createClient } from 'redis'
import { config } from '#app/config'
import { logger } from '#app/utils/logger'
let redisClient: RedisClientType
let isReady: boolean
const cacheOptions = {
url: config.redis.tlsFlag ? config.redis.urlTls : config.redis.url,
}
if (config.redis.tlsFlag) {
Object.assign(cacheOptions, {
socket: {
// keepAlive: 300, // 5 minutes DEFAULT
tls: false,
},
})
}
async function getCache(): Promise<RedisClientType> {
if (!isReady) {
redisClient = createClient({
...cacheOptions,
})
redisClient.on('error', err => logger.error(`Redis Error: ${err}`))
redisClient.on('connect', () => logger.info('Redis connected'))
redisClient.on('reconnecting', () => logger.info('Redis reconnecting'))
redisClient.on('ready', () => {
isReady = true
logger.info('Redis ready!')
})
await redisClient.connect()
}
return redisClient
}
getCache().then(connection => {
redisClient = connection
}).catch(err => {
// eslint-disable-next-line #typescript-eslint/no-unsafe-assignment
logger.error({ err }, 'Failed to connect to Redis')
})
export {
getCache,
}
anyway... in your situation try to handle the error event
client.on('error', err => logger.error(`Redis Error: ${err}`))

How can I gracefully handle a failed tcpSocket.connect attempt?

The following code causes an error when there is no existing TCP server to communicate with on the specified host:
const net = require('net');
const argv = require('minimist')(process.argv.slice(2));
try {
var tcpSocket = new net.Socket();
tcpSocket.connect(argv.tcpport, argv.tcphost, function onConnected() {
console.log('connected');
tcpSocket.on('data', function onIncoming(data) {
console.log(data);
});
tcpSocket.on('close', function onClose(data) {
tcpSocketConnected = false;
});
tcpSocketConnected = true;
});
} catch (err) {
console.log("PRINT ME: ", err);
}
Error:
events.js:183
throw er; // Unhandled 'error' event
^
Error: connect ECONNREFUSED 127.0.0.1:1906
at Object._errnoException (util.js:992:11)
at _exceptionWithHostPort (util.js:1014:20)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1186:14)
I am unable to catch the error even though I wrap the code in a try...catch.
Why does my catch block not catch the error?
How can I gracefully handle the error?
You should be able to explicitly handle the error event using event emitter api (same way as you handled close and data):
tcpSocket.on('error', handleError)
From Docs:
Event: 'error'#
Added in: v0.1.90
<Error>
Emitted when an error occurs. Unlike net.Socket, the 'close' event
will not be emitted directly following this event unless server.close()
is manually called. See the example in discussion of server.listen().

NodeJS -> Error: write after end (only after first request)

In my express app i have the route below:
router.get('/generatedData', function (req, res) {
res.setHeader('Connection' , 'Transfer-Encoding');
res.setHeader('Content-Type' , 'text/html; charset=utf-8');
res.setHeader('Transfer-Encoding' , 'chunked');
var Client = someModule.client;
var client = Client();
client.on('start', function() {
console.log('start');
});
client.on('data', function(str) {
console.log('data');
res.write(str);
});
client.on('end', function(msg) {
client.stop();
res.end();
});
client.on('err', function(err) {
client.stop();
res.end(err);
});
client.on('end', function() {
console.log('end');
});
client.start();
});
On first call everything works fine (console)
We've got ourselves a convoy on port 3000
start
data
data
data
data
data
...
data
end
GET /generatedData 200 208.426 ms - -
I get all the data and res.end() is being called and successfully closes the request.
The problem starts after first request. I make the exact same request (new one of course) and i get the following error (console):
start
data
data
data
events.js:160
throw er; // Unhandled 'error' event
^
Error: write after end
at ServerResponse.OutgoingMessage.write (_http_outgoing.js:439:15)
at Client.<anonymous> (/Users/xxxx/projects/xxxx/routes/index.js:33:17)
at emitOne (events.js:96:13)
at Client.emit (events.js:188:7)
at FSWatcher.<anonymous> (/Users/xxxx/projects/xxxx/lib/someModule.js:116:32)
at emitTwo (events.js:106:13)
at FSWatcher.emit (events.js:191:7)
at FSEvent.FSWatcher._handle.onchange (fs.js:1412:12)
[nodemon] app crashed - waiting for file changes before starting...
This happens without res.end() being called.
I manage to get some data before the crash.
How can i get this error without res.end() being called at all?
Do i somehow save the previous res instance?
Thanks,
Asaf
Have the same problem. My module was extened by EventEmitter and each time i catch event in router - it stays there, end on second call there are two eventlisteners not one. Setting "once" instead of "on" - worked for me.
client.once('start', function() {
console.log('start');
});
instead of
client.on('start', function() {
console.log('start');
});

Reconnect to TCP/IP socket on NodeJS

I use "net" library to create TCP connection on my nodeJs.
root.socket = net.createConnection(root.config.port, root.config.server);
I'm trying to handle error when remote server is down and reconnect in Cycle.
root.socket.on('error', function(error) {
console.log('socket error ' + error);
root.reconnectId = setInterval(function () {
root.socket.destroy();
try {
console.log('trying to reconnect');
root.socket = net.createConnection(root.config.port, root.config.server);
} catch (err) {
console.log('ERROR trying to reconnect', err);
}
}, 200);
}
The trouble is that in case of remote server shutdown I still get en error and my nodeJS server stops.
events.js:72
throw er; // Unhandled 'error' event
^ Error: connect ECONNREFUSED
at errnoException (net.js:904:11)
at Object.afterConnect [as oncomplete] (net.js:895:19)
You will need something like this:
var net = require('net');
var c = createConnection(/* port, server */);
function createConnection(port, server) {
c = net.createConnection(port, server);
console.log('new connection');
c.on('error', function (error) {
console.log('error, trying again');
c = createConnection(port, server);
});
return c;
}
In your case you are creating a new connection but you don't attach any error listener, the error is raised somewhere else in the execution loop and can not be caught by the "try / catch" statement.
P.S. try to avoid using "try / catch" statement, error handling in Node.JS is made using error listeners and domains, it can be useful only for JSON.parse() or other functions that are executed synchronously.

How to deal with 'read ETIMEDOUT' in Node.js?

I have a pub/sub model using Node.js to transmit data from one client to another client. Besides, the server also records everything received and sends it to new clients.
However, some data corrupted when transfer, and I got error like:
Error with socket!
{ [Error: write EPIPE] code: 'EPIPE', errno: 'EPIPE', syscall: 'write' }
Error with socket!
{ [Error: read ETIMEDOUT] code: 'ETIMEDOUT', errno: 'ETIMEDOUT', syscall: 'read' }
I don't know how to properly handle these errors. It looks like the client is down.
Since the server is only a proxy like a server, it doesn't really know what data means. I have no idea how to validate every data pack before meeting these errors.
Here is my code:
// server is an object inheriting from net.Server
server.on('listening', function() {
var port = server.address().port;
}).on('connection', function(cli) {
cli.socketBuf = new Buffers();
cli.commandStarted = false;
cli.dataSize = 0;
cli.setKeepAlive(true, 10*1000);
cli.setNoDelay(true);
cli.on('connect', function() {
server.clients.push(cli);
}).on('close', function() {
var index = server.clients.indexOf(cli);
server.clients.splice(index, 1);
}).on('data', function (buf) {
server.emit('data', cli, buf);
if(op.autoBroadcast) {
_.each(server.clients, function(c) {
if(c != cli) c.write(buf);
});
}
}).on('error', function(err) {
console.log('Error with socket!');
console.log(err);
});
}).on('error', function(err) {
console.log('Error with server!');
console.log(err);
});
// ...
// room.dataSocket is an instance of server beyond
room.dataSocket.on('data', function(cli, d) {
// bf is a buffered file
bf.append(d);
room.dataFileSize += d.length;
}).on('connection', function(con){
bf.readAll(function(da) {
con.write(da);
});
});
If you get an EPIPE or indeed any error when writing, the peer has closed or the connection has been dropped. So you must close the connection at that point.
If you get a read timeout the inference is that either you have set an unrealistically short timeout or else the peer has failed to deliver in time: in the second case once again you should assume the connection is down, and close it.

Resources