4

我正在从 Java 启动一个外部进程并通过process.getInputStream()等获取它的标准输入、标准输出和标准错误。我的问题是:当我想将数据写入我的输出流(proc 的标准输入)时,直到我实际调用它才会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 标准输出的任何输入,我希望在第一次运行循环时看到它,看到尝试从 Java->Perl 发送数据,然后什么也没有。但实际上,当写入输出流时,我实际上只看到来自 Perl 的初始消息(在该 OUTPUT 消息之后)。有什么我不知道的阻塞吗?

非常感谢任何帮助!

4

3 回答 3

3

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

HANDLE->autoflush( EXPR )
$OUTPUT_AUTOFLUSH
$| 
于 2012-07-18T06:44:59.213 回答
0

Perl 可能会在开始打印任何内容之前对其进行缓冲。

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

不,它没有。它具有将 1 和bytes.length-1字节之间的值读入数组的效果。请参阅 Javadoc。

于 2012-07-18T03:44:07.230 回答
0

我在您的代码中看不到任何明显的缓冲,因此它可能在 Perl 方面。如果\n在打印语句的末尾添加换行符会发生什么?

另请注意,一般情况下,您不能像那样在主线程上读取标准输入和标准错误。您将陷入死锁 - 例如,如果子进程打印大量 stderr,而父进程正在读取 stdin,则 stderr 缓冲区将填满,子进程将阻塞,但父进程将永远处于阻塞状态,试图读取 stdin。

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

于 2012-07-18T04:58:13.973 回答