Java - 刷新进程的OutputStream如果数据太小则不会立即发送数据

时间:2012-07-18 02:46:41

标签: java outputstream

我正在从Java启动一个外部进程并通过process.getInputStream()抓取它的stdin,stdout和stderr等。我的问题是:当我想将数据写入我的输出流(proc的stdin)时,它不是收到发送,直到我实际上在流上调用close()。我明确地打电话给flush()

我做了一些实验并注意到如果我增加了我发送的字节数,它最终会通过。我系统上的幻数是 4058 字节。

测试我将数据发送到perl脚本,如下所示:

#!/usr/bin/perl
use strict;
use warnings;

print "Perl starting";

while(<STDIN>) {
    print "Perl here, printing this: $_" 
}

现在,这是java代码:

import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;

public class StreamsExecTest {


    private static String readInputStream(InputStream is) throws IOException {
        int guessSize = is.available();
        byte[] bytes = new byte[guessSize];
        is.read(bytes); // This call has side effect of filling the array
        String output = new String(bytes);
        return output;
    }

    public static void main(String[] args) {

        System.out.println("Starting up streams test!");

        ProcessBuilder pb;
        pb = new ProcessBuilder("./test.pl");


        // Run the proc and grab the streams
        try {
            Process p = pb.start();
            InputStream pStdOut = p.getInputStream();
            InputStream pStdErr = p.getErrorStream();
            OutputStream pStdIn = p.getOutputStream();


            int counter = 0;
            while (true) {  

                String output = readInputStream(pStdOut);
                if (!output.equals("")) {
                    System.out.println("<OUTPUT> " + output);
                }

                String errors = readInputStream(pStdErr);
                if (!errors.equals("")) {
                    System.out.println("<ERRORS> " + errors);
                }

                if (counter == 50) {
                    // Write to the stdin of the execed proc.  The \n should
                    // in turn trigger it to treat it as a line to process
                    System.out.println("About to send text to proc's stdin");
                    String message = "hello\n";

                    byte[] pInBytes = message.getBytes();
                    pStdIn.write(pInBytes);
                    pStdIn.flush();
                    System.out.println("Sent " + pInBytes.length + " bytes.");

                }

                if (counter == 100) {
                    break;
                }

                Thread.sleep(100);
                counter++;
            }
            // Cleanup
            pStdOut.close();
            pStdErr.close();
            pStdIn.close();
            p.destroy();

        } catch (Exception e) {
            // Catch everything
            System.out.println("Exception!");
            e.printStackTrace();
            System.exit(1);
        }

    }
}

因此,当我运行这个时,我实际上得不到任何回报。如果在调用flush()后立即调用pStdIn上的close(),它会按预期工作。这不是我想要的;我希望能够不断地保持流开放并随时写信给我。如前所述,如果消息是4058字节或更大,这将在没有close()的情况下工作。

操作系统(在64位Linux上运行,64位Sun JDK是否值得)在发送之前缓冲数据?我可以看到Java无法真正控制它,一旦JVM使系统调用写入管道,它所能做的只是等待。还有另一个难题:

Perl脚本在进入while循环之前打印行。因为我在我的Java循环的每次迭代中检查来自Perl的stdout的任何输入,我希望在第一次循环运行时看到它,看到尝试从Java-&gt; Perl发送数据然后什么都没有。但实际上,当对输出流的写入发生时,我只看到来自Perl的初始消息(在该OUTPUT消息之后)。阻塞的东西是我不知道的吗?

任何帮助都非常感谢!

3 个答案:

答案 0 :(得分:3)

你没有告诉Perl使用无缓冲输出。查看perlvar并搜索$|以了解设置无缓冲模式的不同方法。实质上,其中一个:

HANDLE->autoflush( EXPR )
$OUTPUT_AUTOFLUSH
$| 

答案 1 :(得分:0)

Perl可能会在开始打印之前缓冲它。

  

is.read(字节); //此调用具有填充数组的副作用

不,不。它具有在1到bytes.length-1字节之间读入数组的效果。见Javadoc。

答案 2 :(得分:0)

我的代码中没有看到任何明显的缓冲,因此它可能位于Perl端。如果在打印声明的末尾添加换行符\n,会发生什么?

另请注意,通常,您不能像这样读取主线程上的stdin和stderr。您将遇到死锁 - 例如,如果子进程打印了大量stderr,而父进程正在读取stdin,则stderr缓冲区将填充并且子进程将阻塞,但是父进程将永远阻止尝试读取stdin。

您需要使用单独的线程来读取stderr和stding(也与主线程分开,这里用于将输入泵送到进程)。