2010-11-20 74 views
2

我正在为Java中的命令行程序编写终端包装,并使用ProcessBuilder产生子进程。要将击键发送到子进程,我只需从GUI直接写入e.getKeyChar()到由proc.getOutputStream()给出的OutputStream即可。为了接收从子进程的输出,我基本上有一个while循环,从读取子的stdoutJava:无法从进程获取标准输出数据,除非手动刷新

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; 
} 

我在Ubuntu 10.10上运行使用Sun Java 6

回答

1

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

但这并非总是可行,特别是在处理第三方代码时。最好的其他修复我知道在这种情况下是使用像Expect这样的工具来欺骗子进程。在内部,Expect知道如何假装成为一个终端(在Unix上使用ptys和在Windows上godawful黑客),以欺骗其他程序关闭(或至少减少)缓冲。对于Expect,有一个脚本 - 非缓冲区 - 使其专注于此类用途。 (通常它可以做的不仅仅是处理不规则的缓冲,但它是最好的解决办法。)

4

,直到数据已被写入到磁盘你不能从文件中读取数据。 直到数据被放入管道/套接字缓冲区后,才能从套接字或管道读取数据。

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

每个网络程序员都必须处理这个问题,所以只需处理它。 (*) - 有时某些程序(如cat)有选项(-u)指示程序使用无缓冲输出。否则你是仁慈的

+0

对,但是用我的Java程序,I/O *永远不会*发送,而如果我手动在终端中运行所述子进程,I/O几乎是即时的。 – erjiang 2010-11-20 17:22:33

+0

是的,我们知道我们必须处理它。问题是如何。 – jononomo 2013-08-19 17:56:24

0

你没有从事件调度线程运行你的I/O读取循环吗?

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

+0

这就是我目前正在做的。我有一个单独的线程从子进程读取(因为'read()'块),它通知主线程中的事件处理程序。键击会立即发送到子流程并刷新。 – erjiang 2010-11-21 00:44:50

相关问题