有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

如果数据太小,Java刷新进程的输出流不会立即发送数据

我正在从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标准输出的任何输入,因此我希望在循环的第一次运行中看到它,看到从Java发送数据的尝试->;Perl,然后什么都没有。但实际上,当写入输出流时,我只看到来自Perl的初始消息(在输出消息之后)。有什么我不知道的阻塞吗

非常感谢您的帮助


共 (3) 个答案

  1. # 1 楼答案

    您还没有告诉Perl使用无缓冲输出。在perlvar中查找$|以查找设置无缓冲模式的不同方法。本质上,以下各项之一:

    HANDLE->autoflush( EXPR )
    $OUTPUT_AUTOFLUSH
    $| 
    
  2. # 2 楼答案

    我在代码中没有看到任何明显的缓冲,因此它可能在Perl端。如果在print语句的末尾添加一个换行符\n,会发生什么情况

    另外请注意,一般来说,您不能像这样读取主线程上的stdin和stderr。您将面临死锁-例如,如果子进程打印了大量的stderr,而父进程正在读取stdin,则stderr缓冲区将被填满,子进程将被阻塞,但父进程将永远被阻塞,试图读取stdin

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

  3. # 3 楼答案

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

    is.read(bytes); // This call has side effect of filling the array

    不,它没有。它的效果是在数组中读取1到bytes.length-1字节。请参阅Javadoc