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

我正在用Java编写命令行程序的终端包装器,并使用ProcessBuilder生成子进程。 要将键e.getKeyChar()e.getKeyChar() ,我只需将GUI中的e.getKeyChar()直接写入e.getKeyChar()给出的OutputStream 。 为了从子进程接收输出,我基本上有一个从子进程的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  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。

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

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

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

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

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

(*) – 偶尔有些程序(比如cat for one)有选项( -u )来指示程序使用无缓冲的输出。 否则你是怜悯的

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

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