I have a node.js server that pretty much runs a child process and sends the stdout from the process to connected clients via sockets. It is working just fine but when a client disconnects I cannot close the childprocess due to the var being outside the scope. Here is my sample code
var sys = require('sys'),
ws = require('./ws');
var server = ws.createServer(function (socket) {
socket.addListener("connect", function (resource) {
var counter = 0;
sys.puts("client connected from " + resource);
var spawn = require('child_process').spawn, tail = spawn('app');
sys.puts("Spawned child pid: "+ tail.pid);
tail.stdout.addListener('data', function(data) {
socket.write(JSON.stringify(data));
});
})
socket.addListener("close", function () {
//
// need to access the tail var here!!!
//
sys.puts("quit");
})
})
server.listen(3656)
In my close listener I need to access the tail variable so I can kill the process.
Move tail declaration one level up, from onConnect to createServer, since it is still the same instance:
ws.createServer(function(socket){
var tail;
socket.addListener("connect", function(resource){
tail = spawn("app");
...
});
socket.addListener("close", function(){
if(tail)
tail = null;
});
});
Related
I'm trying to send sensor information from instance of NodeJs to another(eventually on another server). When the while loop in the client code below is enable no connection or data is sent to the server. However when the while loop is commented out I get a connection and data. It's only a single object of data but that's expected when not pulling the sensor data in a loop.
Server Code:
'use strict';
const net = require('net');
const PORT = 5000;
const ADDRESS = '127.0.0.1';
let server = net.createServer(onClientConnected);
server.listen(PORT, ADDRESS);
function onClientConnected(socket) {
console.log(`New client: ${socket.remoteAddress}:${socket.remotePort}`);
socket.destroy();
}
console.log(`Server started at: ${ADDRESS}:${PORT}`);
function onClientConnected(socket) {
let clientName = `${socket.remoteAddress}:${socket.remotePort}`;
console.log(`${clientName} connected.`);
socket.on('data', (data) => {
let m = data.toString().replace(/[\n\r]*$/, '');
var d = {msg:{info:m}};
console.log(`${clientName} said: ${data.toString()}`);
socket.write(`We got your message (${m}). Thanks!\n`);
});
socket.on('end', () => {
console.log(`${clientName} disconnected.`);
});
Client Code:
var n = require('net');
var s = n.Socket();
s.connect(5000, 'localhost');
var i2c = require('i2c-bus');
var MPU6050 = require('i2c-mpu6050');
var address = 0x68;
var i2cl = i2c.openSync(1);
var sensor = new MPU6050(i2cl, address);
//while (true) {
var data = sensor.readSync();
console.log(data);
s.write(data.toString());
//}
Node's main event loop is single threaded so only a single thing is executing at any one time. A particular thread won't relinquish control back to the event loop to continue processing more events until it's finished. Socket.write doesn't actually write to the socket when it's called; instead it creates a task in the event loop queue. The while loop causes the current thread to never exit and effectively blocks the event loop, so the subsequent socket write events are never processed.
Replacing your client code with this should work and achieve effectively what you're trying to do:
var n = require('net');
var s = n.Socket();
s.connect(5000, 'localhost');
var i2c = require('i2c-bus');
var MPU6050 = require('i2c-mpu6050');
var address = 0x68;
var i2cl = i2c.openSync(1);
var sensor = new MPU6050(i2cl, address);
const readData = () => {
var data = sensor.readSync();
console.log(data);
s.write(data.toString());
setImmediate(readData);
}
readData();
setImmediate says to execute the function AFTER any other events have processed, in this case it will be the event from Socket.write. The Socket.write function also provides a callback that is called once the data is done writing to the socket.
How can I manage multiple concurrent child processes that have been forked?
In this example, start_child() can be invoked multiple times, and each invocation can run indefinitely. When forking an arbitrary number of child processes like this, how can I communicate with / address each individual child process? Let's say I have 3 forked child processes running, and they keep running indefinitely, but I want to kill (or send a message to) child process number 2. How would I do that?
If stop_child() is invoked, it kills all of the currently running child processes. How do I refactor this code so that I can call stop_child() on an individual child process?
let app = require('express')();
let server = require('http').Server(app);
let io = require('socket.io')(server);
let fork = require('child_process').fork;
server.listen(80);
app.get('/', function(request, response) {
response.sendFile(__dirname + '/index.html');
});
io.on('connection', function(socket) {
socket.on('start_child', function () {
start_child();
});
socket.on('stop_child', function () {
child.kill();
}
}
function start_child() {
child = fork('./child_script.js');
//conditional logic to either run
//start_child() again or let it end
}
UPDATE
I tried this, based on some of the comments. But if I launch 3 processes and then call, for example, child[0].kill(), I get an error: Cannot read property 'kill' of undefined. I'm guessing my problem is that I'm not correctly passing the i variable to the io.on() call:
let app = require('express')();
let server = require('http').Server(app);
let io = require('socket.io')(server);
let fork = require('child_process').fork;
let i = 0;
let child = [];
server.listen(80);
app.get('/', function(request, response) {
response.sendFile(__dirname + '/index.html');
});
io.on('connection', function(socket) {
socket.on('start_child', function (i) {
start_child(i++);
});
//this is hard coded just for testing
socket.on('stop_child', function () {
child[0].kill();
}
}
function start_child(i) {
child[i] = fork('./child_script.js');
//conditional logic to either run
//start_child() again or let it end
}
UPDATE #2
Okay, I figured out that I need to send the incrementing variable from the client side, passed through an object coming from the emit call. Now when I call child[0].kill() there is no error. The problem is that the child process is not killed:
server.js
let app = require('express')();
let server = require('http').Server(app);
let io = require('socket.io')(server);
let fork = require('child_process').fork;
server.listen(80);
app.get('/', function(request, response) {
response.sendFile(__dirname + '/index.html');
});
io.on('connection', function(socket) {
socket.on('start_child', function (count) {
let num = count.count;
start_child(num);
});
//this is hard coded just for testing
socket.on('stop_child', function (count) {
let num = count.count;
child[num].kill();
}
}
function start_child(num) {
child[num] = fork('./child_script.js');
//conditional logic to either run
//start_child() again or let it end
}
index.html
$(function () {
let socket = io();
let i = 0;
$('#start').on('click', function () {
socket.emit('start_child', {"count": i++});
});
$('#stop').on('click', function () {
//the count is hard coded here just for testing purposes
socket.emit('stop_child', {"count": 0});
});
});
FINAL UPDATE - WITH RESOLUTION
Resolution #2 (right above this) is actually the solution. The problem I was having after that (where the child.kill() call didn't seem to do anything) was caused by a piece of the code that I had left out (in the code comment: 'conditional logic to either run start_child() again or let it end').
This is what was in there:
if (condition) {
setTimeout(start_child, 5000);
} else {
console.log('this child process has ended');
}
And this is what I changed it to (basically, I just had to pass the incrementing variable to the start_child() function so that it would have the same place in the child array when it restarted):
if (condition) {
setTimeout(function() {
start_child(num)
}, 5000);
} else {
console.log('this child process has ended');
}
fork() returns a ChildProcess object which has methods and events on that object for interprocess communication with that process. So you have to save each ChildProcess object from when you call start_child() and then use the appropriate object in order to communicate with the other process.
You can see events and methods of the ChildProcess object here. There are also numerous code examples.
In order to send message to the child process, use the following example :
parent.js
**
const { fork } = require('child_process');
const forked = fork('child.js');
forked.on('message', (msg) => {
console.log('Message from child', msg);
});
forked.send({ hello: 'world' });
**
child.js
process.on('message', (msg) => {
console.log('Message from parent:', msg);
});
let counter = 0;
setInterval(() => {
process.send({ counter: counter++ });
}, 1000);
For further information refer this article :
https://medium.freecodecamp.com/node-js-child-processes-everything-you-need-to-know-e69498fe970a
I have a node.js server, which will print out some message in the console and then start the server.
I am creating a automation test by using tap to check the message in the console.log and check if server is started, i.e. there is a PID generated.
I tried 2 different methods -child_process.exec and child_process.spawn
1. Use child_process.exec with a call back function.
This does not work as the server is long running and will not even
go to the call back, so I cannot even check for any stdout.
Then I use child_process.exec without call back, this solves the
first issue where I can now get the message back from stdout.
The second issue is that the test will hang since the server is long running and will not terminate by itself.
code snippet:
var exec = require('child_process').exec;
tap.test('test server start', function(t) {
childProcess= exec('node',['server']);
console.log('[exec] childProcess.pid: ', childProcess.pid);
t.notEqual(childProcess.pid, undefined);
childProcess.stdout.on('data', function (data) {
console.log('[exec] stdout: ', data.toString());
t.match(data.toString(), "Example app listening at http://:::3000");
t.end();
childProcess.kill('SIGTERM');
});
childProcess.stderr.on('data', function (data) {
console.log('[exec] stderr: ', data.toString());
});
childProcess.on('close', function (code) {
if (code!=null)
console.log('child process exited with code '+ code);
});
});
use child_process.spawn -code snippet
var spawn = require('child_process').spawn;
tap.test('test server start', function(t) {
childProcess= spawn('node',['server']);
console.log('[spawn] childProcess.pid: ', childProcess.pid);
t.notEqual(childProcess.pid, undefined);
childProcess.stdout.on('data', function (data) {
console.log('[spawn] stdout: ', data.toString());
t.match(data.toString(), "Example app listening at http://:::3000");
t.end();
childProcess.kill('SIGTERM');
});
childProcess.stderr.on('data', function (data) {
console.log('[spawn] stderr: ', data.toString());
});
childProcess.on('close', function (code) {
if (code!=null)
console.log('child process exited with code '+ code);
});
});
In both 1 & 2, the test will hang since the server is long running,
I need to use child_process.kill() to terminate the test
Is there a better method to achieve this?
Thanks in advance for any improvements.
Well, I think that you can check if the server is alive in a different way (without spawning a new process).
For example, you can start your server waiting for connections:
const net = require('net');
var connections = {};
var server = net.createServer(function(conn) { });
server.listen(3333);
server.on('connection',function(conn) {
var key = conn.remoteAddress + ':' + conn.remotePort;
connections[key] = conn;
conn.on('close',function() {
delete connections[key];
});
});
Then, connect some clients (or just one) to the server:
var connected = 0;
for (var i = 0;i < 10;i++) {
var client = net.connect(3333);
client.on('connect',function() {
connected++;
console.log(connected);
});
}
So, if you are be able to connect to the server, then your server is alive.
And finally, when you want to close the server, just create a new function like this one:
var destroy = function ()
{
server.close(function() {
console.log('ok');
});
for (var key in connections) {
connections[key].destroy();
}
}
Call it for example after 10 successful connections to the server. Inside the for loop:
for (var i = 0; i < 10; i++) {
var client = net.connect(3333);
client.on('connect',function() {
connected++;
if (connected === 10) {
destroy();
}
});
}
This is a very basic example, but I think that it's enough to understand another way to do what you want to do.
I.
My situation is as follows: there's an array of IP address. I will test each IP to connect to a remote server. If one IP connects, the rest IPs are ignored and not going to be connected.
I used the following Node.JS codes to do the work, but it seems not working. Please give some hints. Thanks!
// serverip is a var of string splitted by ";", e.g. "ip1;ip2;ip3"
var aryServerIP = serverip.split(";");
console.log(aryServerIP);
var ipcnt = aryServerIP.length; // ipcnt = 3, for example
for (ip in aryServerIP)
{
console.log("to process: " + ipcnt); // error here: always print 3
var net = require('net');
var client = new net.Socket();
var rdpport = 3389;
client.connect(rdpport, aryServerIP[ip], function(){
console.log("socket connected to " + aryServerIP[ip] + ":" + rdpport);
client.destroy();
if (0 != ipcnt)
{
// do some real connection work about aryServerIP[ip].
ipcnt--;
}
});
client.on('error', function(){
console.log("fail to connect to " + aryServerIP[ip] + ":" + rdpport);
ipcnt--;
});
}
I know using ipcnt count to control the loop is bad, but I don't know how to control the Node.JS loop, when there's async function called in the loop...
Because your connect and error callbacks are both asynchronous, so they will both run after the for loop has completely finished.
What you need to do is set up a set of callbacks. For instance, rather than use a for loop, wrap your entire loop body in a function. If connect succeeds, then just do what you normally would, and if the error callback is called, then execute the wrapping function again. Something like this:
var async = require('async');
var net = require('net');
var rdpport = 3389;
function tryConnections(aryServerIP, callback){
function connect(i){
if (i === aryServerIP.length) return callback();
var client = new net.Socket();
client.connect(rdpport, aryServerIP[i], function(){
callback(client);
});
client.on('error', function(){
connect(i + 1)
});
}
connect(0)
}
tryConnections(serverip.split(";"), function(client){
if (client) // Successfully connected to something
else // all ips failed
});
Another solution would be to use the Async library.
function tryConnections(aryServerIP, callback){
async.detectSeries(aryServerIP, function(ip, cb){
var client = new net.Socket();
client.connect(rdpport, ip, function(){
cb(client);
});
client.on('error', function(){
cb();
});
}, callback);
}
I want to pass a command to a python child process and then get the result. I would use exec, but I want to keep the child process open so that I don't have to open it every time I do a new command. Here is my code that currently does nothing:
var connect = require('connect'),
io = require("socket.io").listen(1032),
util = require("util"),
child = require('child_process'),
python = child.spawn("python");
var app = connect()
.use(connect.static(__dirname + '/www'))
.use(connect.logger('dev'))
.listen(3000);
io.sockets.on('connection', function (socket) {
console.log("Socket " + socket.id + " opened");
python.stdout.on('data', function (data) {
console.log("computed", data.toString("utf-8"));
socket.emit("python", { result : data.toString("utf-8") });
});
socket.on('python', function (data) {
console.log("received data" + data.cmd);
python.stdin.resume();
python.stdin.write(data.cmd);
python.stdin.end();
});
});
Does your python code contain any non-ascii characters?
This works fine:
var
spawn = require('child_process').spawn,
python = spawn('python');
python.stdin.write('print ("a")');
python.stdin.end();
python.stdout.on('data', function (data) {
console.log(data.toString());
});
But if I change the letter "a" to a russian letter "п" it stops working. Event not fired.
But it works perfect with node interpreter (with any utf8 characters).
var
spawn = require('child_process').spawn,
node = spawn('node');
node.stdin.write('console.log("п");');
node.stdin.end();
node.stdout.on('data', function (data) {
console.log(data.toString());
});
I think you need to ask about it in python section.