如何将无缓冲的数据从 STDOUT 管道传输到 ServerResponse?

4
我该如何将子进程的非缓冲输出导入到HTTP.ServerResponse中呢?
以下Node.js代码可以处理缓冲输出,但无法处理非缓冲输出。
var http = require('http'),
    spawn = require('child_process').spawn;

http.createServer(function (req, res) {
    req.on('data', function (data) {
        var ping = spawn("ping", ["127.0.0.1"]);
        ping.stdout.pipe(res);
        ping.stderr.pipe(res);
        req.connection.on('end', function() {
            ping.kill();
        });
    });
});

以下是运行ping 127.0.0.1的输出结果:
ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1): 56 data bytes
Request timeout for icmp_seq 0
Request timeout for icmp_seq 1

由于ping将请求超时写入标准输出作为非缓冲数据,因此上述代码仅在ping成功时才有效。

我正在使用Node.js v0.8.19。


我不明白问题在哪里,您无法在客户端上获取ping的输出吗? - user568109
我无法在客户端上获得ping超时。尽管我可以得到正常的ping响应。 - Russ
ICMP序列x的请求超时未显示。 - user568109
2个回答

5
这是一个常见问题,不一定局限于Node.js。
如果你使用Unix系统,你可以使用unbuffer脚本,它是Expect的一部分。大多数GNU系统也提供stdbuf,让你做同样的事情(尽管我承认我对它并不是很熟悉)。
如果你想使用unbuffer,你只需要将上面的例子改为:
var ping = spawn("unbuffer", ["ping", "127.0.0.1"]);

如果你使用的是Windows系统,那么情况会更加困难,因为Windows没有本地提供类似PTY的等效功能。像winpty(以及其JavaScript绑定pty.js)这样的项目尝试使用Windows控制台API来重新创建此功能,尽管我发现它们的实现有些错误和不完整。 ActiveState的win32 Expect软件包不太容易安装,并且默认分发中不包含unbuffer脚本。但是,如果您成功安装并对Unix unbuffer脚本进行一些小修改,就可以编译出一个unbuffer可执行文件,然后可以从Node中调用它。

1
如果可以的话,我会给它加上+1,000,000分。我差点就要把我的拳头砸进电脑里了。 "unbuffer" 是解决方案!另外(为了让这个评论有一些内容),对于使用OSX的用户来说,安装很容易:brew install expect - Bailey Parker
实际上我无法让stdbufunbuffer一样工作,有人能确认吗? - RushPL
1
请查看此链接http://unix.stackexchange.com/a/61833/23420 - 它说要在默认安装的script -c中运行程序,是的! - RushPL

0

当请求成功接收到服务器时,您正在终止ping进程。 ping在输出icmp_seq 5的请求超时之前就关闭了。

尝试像这样做

var http=require('http');
var spawn = require('child_process').spawn;
http.createServer(function (req, res) {
        var ping = spawn("ping", ["127.0.0.1","-c","20"]);
        ping.stdout.pipe(res);
        ping.stderr.pipe(res);
}).listen(8080);

注意: ping 命令会一直执行下去,因此您不能等待它结束。请使用 -c 选项来指定 ping 的次数。

使用 -c 确实可以强制结束进程,此时客户端会收到结果。然而,我希望能够在进程进行中获取结果(对于成功的 ping 是有效的)。 - Russ
响应只会针对特定请求发送一次。因此,您不能在发送第一个响应后连续发送结果。要流式传输结果,您将需要使用socket.io,这将帮助您在服务器和客户端之间实时进行通信。 - user568109
那是不正确的。只要连接保持,服务器就可以继续向客户端发送更多数据。您可以通过成功的ping来观察到这种行为。 - Russ
你混淆了响应流和HTTP响应。HTTP响应只发送一次,这是在浏览器上显示的时间。流允许您编写更多数据,但仅在响应流完成后才发送到浏览器。因此,它无法显示实时结果。 - user568109
我正在使用命令行CURL客户端,默认情况下使用HTTP Keep-Alive。Keep-Alive实现了持久连接(在这里定义:http://tools.ietf.org/html/rfc2068#section-8),可用于在同一连接上发送和接收多个请求和响应。每次成功的ping发生时,输出被传输到响应套接字并立即传输到客户端,而不是连接完成时。不幸的是,对于不成功的ping,输出是无缓冲区的。 - Russ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接