Java:除非手动刷新,否则无法从Process获取stdout数据

时间:2010-11-20 02:08:33

标签: java process io buffer

我正在用Java编写命令行程序的终端包装器,我使用ProcessBuilder生成子进程。要将击键发送到子流程,我只需将GUI中的e.getKeyChar()直接写入OutputStream给出的proc.getOutputStream()。为了从子进程接收输出,我基本上有一个从子进程的stdout读取的while循环:

while ((b = br.read()) != -1) {
    System.out.println("Read "+b);
    bb[0] = (byte) b;
    // call an event listener with the read byte
    listener.dataReceived(bb);
}

如果我立即刷新两个上的输出,则。也就是说,我必须刷新每个用户输入,并且子进程必须刷新自己的stdout才能发生这些事情。否则,read()阻塞,等待从未实际发送的数据(子进程'stdout只是保持缓冲)。我怎样才能进行I / O?

示例终端子进程:

#include <stdio.h>

int main() {
    char c;
    while((c = getchar()) != -1) {
        printf("Got: %d\n", c);
        // doesn't work in my Java program if the next line isn't present
        fflush(stdout);
    }
    return 0;
}

我正在使用Sun Java 6运行Ubuntu 10.10。

3 个答案:

答案 0 :(得分:4)

在将数据写入磁盘之前,无法从文件中读取数据。 在将数据放入管道/套接字缓冲区之前,您无法从套接字或管道中读取数据。

当外部进程刷新其输出并将数据写入磁盘/管道缓冲区/套接字缓冲区时,您的java程序无法控制(*)。你完全受外部程序缓冲行为的支配。在每个操作系统和每种编程语言中都是如此。

每个网络程序员都必须处理这个问题,所以只需处理它。

(*) - 偶尔有些程序(如cat)有选项(-u)来指示程序使用无缓冲输出。

是你的怜悯

答案 1 :(得分:1)

许多运行时库(例如,我知道libc这样做,并且如果其他人也这样做也不会感到惊讶)将默认缓冲输出除了输出到终奌站。当处理许多行(例如,在正常流水线中)时,这极大地提高了数据处理的效率,但是当只有少量信息时,它会伤害很多。如果您可以访问子进程的源代码,那么最好通过关闭缓冲或添加刷新来更新代码。

但这并非总是可行,尤其是在处理第三方代码时。在这种情况下,我知道的最好的其他修复是使用像Expect这样的工具来欺骗子进程。在内部,Expect知道如何假装成一个终端(在Unix上使用ptys和在Windows上使用神秘的黑客),因此欺骗其他程序关闭(或至少减少)他们的缓冲。 Expect有一个脚本 - unbuffer - 专门针对这种用途。 (一般来说,它可以做的不仅仅是处理不守规矩的缓冲,但无论如何它都是最好的解决方案。)

答案 2 :(得分:0)

您没有从事件派发线程运行I / O读取循环吗?

您应该在一个单独的线程中运行从子进程读取的I / O(如果您还没有这样做)。每个GUI按键立即刷新到子进程可能是最好的;除非你想支持某种“一次读整行”的事情。

相关问题