【问题标题】:Reuse an InputStream to a Process in Java将 InputStream 重用到 Java 中的进程
【发布时间】:2016-02-01 05:54:07
【问题描述】:

我正在使用 ProcessBuilder 通过 Java 输入和接收来自 C++ 程序的信息。启动该过程一次后,我希望能够输入新字符串并接收它们的输出,而无需重新启动整个过程。这是我迄今为止采取的方法:

public void getData(String sentence) throws InterruptedException, IOException{
        InputStream stdout = process.getInputStream();
        InputStreamReader isr = new InputStreamReader(stdout);
        OutputStream stdin = process.getOutputStream();
        OutputStreamWriter osr = new OutputStreamWriter(stdin);
        BufferedWriter writer = new BufferedWriter(osr);
        BufferedReader reader = new BufferedReader(isr);

        writer.write(sentence);
        writer.close();

        String ch = reader.readLine();
        preprocessed="";
        while (ch!=null){
            preprocessed = preprocessed+"~"+ch;
            ch = reader.readLine();
        }

        reader.close();
}

每次我想向正在运行的进程发送输入时,我都会调用此方法。但是,有一个问题:我第一次发送输入,很好,输出接收完美。但是,第二次调用它时,我收到错误

java.io.IOException: Stream closed

这是出乎意料的,因为当再次调用该方法时,理论上所有内容都会重新创建。此外,删除关闭 BufferedWriter 的行会导致代码在下一行停止,就好像 BufferedReader 正在等待 BufferedWriter 关闭一样。

最后一件事 - 即使我创建了一个 NEW BufferedWriter 并指示该方法在第二次调用时使用它,我也得到了同样的异常,我完全不明白。

有什么办法可以解决吗?

非常感谢!

【问题讨论】:

    标签: java stream ioexception processbuilder bufferedwriter


    【解决方案1】:

    发生意外的 IOException 是因为当 Readers 和 Writers 关闭时,它们会依次关闭其底层流。

    当您第一次调用您的方法时,似乎一切正常。但是你关闭了 writer,它关闭了进程输出流,从进程的角度来看,它关闭了 stdin。不确定您的 C++ 二进制文件是什么样的,但可能在完成所有输入后它会愉快地退出。

    因此对您的方法的后续调用不起作用。

    在 Reader 端有一个单独但类似的问题。您调用 readLine() 直到它返回 null,这意味着 Reader 已经感觉到流的结束。但这只发生在进程完全使用其标准输出完成时。

    您需要某种方式来确定您何时处理完一个工作单元(无论您所说的“句子”是什么意思),而无需等待整个流结束。流没有输出之间的逻辑暂停的概念。这只是一个连续的流。 Reader 和 Writer 只是在字节和字符之间缓冲的薄薄的一层,但基本上与流相同。

    也许输出可能有分隔符。或者您可以在实际发送输出之前发送每个输出块的长度并以这种方式区分输出。或者您可能提前知道每次回复需要多长时间?

    您只能通过流获得一枪。所以他们将不得不比这种方法活得更久。如果您想避免每次都重新启动进程,则不能打开和关闭流。 (进程还有其他通信方式,例如套接字,但这可能超出了范围。)

    在正交说明中,在累积输出时,附加到 StringBuilder 通常比字符串连接的大循环更有效。

    您可能还需要一些线程检查 process.exitValue() 或以其他方式确保进程按预期工作。

    【讨论】:

      【解决方案2】:

      不要一直尝试创建和关闭您的信息流,因为一旦您关闭它,它就会永久关闭。创建一次,然后在您的 getData(...) 方法中使用现有的 Streams。仅在完全使用完 Streams 或它们的包装类后才关闭它们。

      请注意,您应该以相同的方法打开和关闭 Stream,因此可能需要其他方法或类来帮助您处理 Stream。考虑为此创建一个 Runnable 类,然后从另一个线程中的流中读取。也不要忽略错误流,因为这可能会发送您需要完全了解这里发生的事情的关键信息。

      【讨论】:

      • 感谢您的快速回复;我很感激!当我在类的构造函数中创建所有内容然后调用该方法时,如果编写器尚未关闭,程序会在“reader.readLine()”行“冻结”,我不知道为什么。我尝试换行并刷新作家,但没有一个奏效。似乎编写器在程序继续运行之前正在等待额外的输入,但我不确定如何在不关闭编写器的情况下告诉它这样做。
      • @sam:如果读者返回null,你的流程不是结束了吗?你怎么知道进程实际上仍在运行?
      • 对不起,我应该澄清一下。该过程是一个 C++ 文件,当在终端中编译和运行时,首先需要大约 50 秒来初始化(这就是我只想做一次的原因),然后接受一个句子作为输入并返回一个输出。您可以继续输入句子并继续接收输出;这个过程不会在一句话之后停止。
      • 有道理!不幸的是,程序的性质要求我无限期地保持流打开(因为它将在服务器上运行并不断等待用户输入)。有没有办法让编写器处于类似“关闭”的状态(我知道不可能完全关闭并重新打开),然后继续编写代码,然后能够循环回到编写器编写再次?非常感谢所有帮助;非常感谢!
      • @Sam:为什么不在线程中继续从 InputStream 中读取数据,并将数据存储在缓冲区或集合中,以便根据需要使用,再次全部在后台线程中。另外我想知道您是否通过关闭编写器并因此过早关闭您的 OutputStream 来伤害自己。让它保持打开状态,直到所有过程完成。再一次,不要忘记错误流。至少通过调用 redirectErrorStream(true) 将其重定向到 InputStream。
      猜你喜欢
      • 2013-09-25
      • 2013-03-25
      • 1970-01-01
      • 2018-08-06
      • 2011-10-25
      • 2011-01-30
      • 2015-07-13
      • 2018-11-20
      • 2011-01-24
      相关资源
      最近更新 更多