【问题标题】:Piping data from child to parent in nodejs在nodejs中将数据从子级管道传输到父级
【发布时间】:2025-12-05 09:10:01
【问题描述】:

我有一个 nodejs 父进程启动另一个 nodejs 子进程。子进程执行一些逻辑,然后将输出返回给父进程。输出很大,我正在尝试使用管道进行通信,正如 child.send() 方法的文档中所建议的那样(顺便说一句,它工作得很好)。

我希望有人建议如何正确建立此沟通渠道。我希望能够从父母向孩子发送数据,也希望能够从孩子向父母发送数据。我已经开始了一点,但它不完整(仅从父母向孩子发送消息)并引发错误。

父文件代码:

var child_process = require('child_process');

var opts = {
    stdio: [process.stdin, process.stdout, process.stderr, 'pipe']
};
var child = child_process.spawn('node', ['./b.js'], opts);

require('streamifier').createReadStream('test 2').pipe(child.stdio[3]);

子文件代码:

var fs =  require('fs');

// read from it
var readable = fs.createReadStream(null, {fd: 3});

var chunks = []; 

readable.on('data', function(chunk) {
    chunks.push(chunk);
});

readable.on('end', function() {
    console.log(chunks.join().toString());
})

上面的代码打印了预期的输出(“test 2”)以及以下错误:

events.js:85
      throw er; // Unhandled 'error' event
            ^
Error: shutdown ENOTCONN
    at exports._errnoException (util.js:746:11)
    at Socket.onSocketFinish (net.js:232:26)
    at Socket.emit (events.js:129:20)
    at finishMaybe (_stream_writable.js:484:14)
    at afterWrite (_stream_writable.js:362:3)
    at _stream_writable.js:349:9
    at process._tickCallback (node.js:355:11)
    at Function.Module.runMain (module.js:503:11)
    at startup (node.js:129:16)
    at node.js:814:3

完整答案:

家长代码:

var child_process = require('child_process');

var opts = {
    stdio: [process.stdin, process.stdout, process.stderr, 'pipe', 'pipe']
};
var child = child_process.spawn('node', ['./b.js'], opts);

child.stdio[3].write('First message.\n', 'utf8', function() {
    child.stdio[3].write('Second message.\n', 'utf8', function() {

    });
}); 

child.stdio[4].pipe(process.stdout);

孩子的密码:

var fs =  require('fs');

// read from it
var readable = fs.createReadStream(null, {fd: 3});

readable.pipe(process.stdout);
fs.createWriteStream(null, {fd: 4}).write('Sending a message back.');

【问题讨论】:

  • 你能包括它抛出的错误吗?

标签: node.js pipe child-process


【解决方案1】:

您的代码可以工作,但是通过使用 streamifier 包从字符串创建读取流,您的通信通道会在传输该字符串后自动关闭,这就是您收到 ENOTCONN 错误的原因。

为了能够通过流发送多条消息,请考虑在其上使用.write。您可以随意调用它:

child.stdio[3].write('First message.\n');
child.stdio[3].write('Second message.\n');

如果您想使用此方法发送多个离散消息(我相信这是基于您之前使用 child.send() 的评论的情况),最好使用一些分隔符来拆分消息当在孩子中读取流时。在上面的示例中,我为此使用了换行符。 event-stream 是一个有用的包来帮助进行这种拆分。

现在,为了从父级中的子级创建另一个通信通道,只需将另一个“管道”添加到您的 stdio。

你可以在孩子里面写:

fs.createWriteStream(null, {fd: 4}).write('Sending a message back.');

并在父级中读取它:

child.stdio[4].pipe(process.stdout);

这将打印“发回消息”。到控制台。

【讨论】:

  • 感谢您的意见。但是,我希望能够在两个方向上进行交流,并且可能进行多次交流。我更喜欢使用 stdout/stderr 来处理其他类型的数据,这就是我在那里使用“管道”的原因。
  • 我完全重写了我的答案,以更好地适应您提出的问题。如果这回答了你的问题,请告诉我:)。
  • 再次感谢您的意见。这帮助我解决了我的问题!
【解决方案2】:

您可以通过fork() 做到这一点

我自己解决了这个问题...fork() 是 spawn 的高级版本,一般建议使用fork() 而不是spawn()

如果您使用{silent:true} 选项,stdio 将通过管道传输到父进程

          const cp = require('child_process');

          const n = cp.fork(<path>, args, {
              cwd: path.resolve(__dirname),
              detached: true,
           });

          n.stdout.setEncoding('utf8');

          // here we can listen to the stream of data coming from the child process:
          n.stdout.on('data', (data) => {
            ee.emit('data',data);
          });

          //you can also listen to other events emitted by the child process
          n.on('error', function (err) {
            console.error(err.stack);
            ee.emit('error', err);
          });

          n.on('message', function (msg) {
            ee.emit('message', msg);
          });

          n.on('uncaughtException', function (err) {
            console.error(err.stack);
            ee.emit('error', err);
          });


          n.once('exit', function (err) {
             console.error(err.stack);
             ee.emit('exit', err);
          });

【讨论】:

    【解决方案3】:

    我遇到了同样的问题并使用 {end:false} 选项来修复错误。不幸的是,接受的答案仅在处理少量数据的离散写入时才有效。如果您有很多数据(而不仅仅是短消息),您需要处理流量控制并且使用 .write() 不是最好的。对于这样的场景(大数据传输),最好在代码中使用 .pipe() 函数来处理流量控制。

    抛出错误是因为父进程中的可读流试图结束并关闭子进程的可写流输入管道。您应该在父进程管道中使用{end: false} 选项:

    原始代码: require('streamifier').createReadStream('test 2').pipe(child.stdio[3]);

    建议修改: require('streamifier').createReadStream('test 2').pipe(child.stdio[3], {end:false});

    在此处查看 NodeJs 文档中的详细信息:https://nodejs.org/dist/latest-v5.x/docs/api/stream.html#stream_readable_pipe_destination_options

    希望这可以帮助其他面临此问题的人。

    【讨论】:

    • 感谢您的意见。我稍后会测试它,如果它有效,我会将您的答案标记为已接受!