我正在用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。
答案 0 :(得分:4)
在将数据写入磁盘之前,无法从文件中读取数据。 在将数据放入管道/套接字缓冲区之前,您无法从套接字或管道中读取数据。
当外部进程刷新其输出并将数据写入磁盘/管道缓冲区/套接字缓冲区时,您的java程序无法控制(*)。你完全受外部程序缓冲行为的支配。在每个操作系统和每种编程语言中都是如此。
每个网络程序员都必须处理这个问题,所以只需处理它。
(*) - 偶尔有些程序(如cat
)有选项(-u
)来指示程序使用无缓冲输出。
答案 1 :(得分:1)
许多运行时库(例如,我知道libc这样做,并且如果其他人也这样做也不会感到惊讶)将默认缓冲输出除了输出到终奌站。当处理许多行(例如,在正常流水线中)时,这极大地提高了数据处理的效率,但是当只有少量信息时,它会伤害很多。如果您可以访问子进程的源代码,那么最好通过关闭缓冲或添加刷新来更新代码。
但这并非总是可行,尤其是在处理第三方代码时。在这种情况下,我知道的最好的其他修复是使用像Expect这样的工具来欺骗子进程。在内部,Expect知道如何假装成一个终端(在Unix上使用ptys和在Windows上使用神秘的黑客),因此欺骗其他程序关闭(或至少减少)他们的缓冲。 Expect有一个脚本 - unbuffer - 专门针对这种用途。 (一般来说,它可以做的不仅仅是处理不守规矩的缓冲,但无论如何它都是最好的解决方案。)
答案 2 :(得分:0)
您没有从事件派发线程运行I / O读取循环吗?
您应该在一个单独的线程中运行从子进程读取的I / O(如果您还没有这样做)。每个GUI按键立即刷新到子进程可能是最好的;除非你想支持某种“一次读整行”的事情。