在Windows中:从C程序获取无缓冲的stdout到Java程序InputStream [现在用最少的例子!]

时间:2014-06-26 21:04:43

标签: java c windows ipc stdout

解决:

HovecraftFullOfEels发现了我的错误。请参阅下面的答案评论。我应该调用start()时在Thread对象上调用run()。在我的原始程序中,这意味着它在stderr上被阻止,直到程序退出然后立即获得stdout。

更新

根据要求,我制作了一个最小的例子。这是制作人:

package producer;

public class Producer {

    public static void main(String[] args) throws InterruptedException {
        for (int i=0; i<10; i++) {
            System.out.println(i);
            System.out.flush();
            Thread.sleep(1000);
        }
    }    
}

这是消费者:

package consumer;

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

public class Consumer {
    static class Dumper extends Thread {
        InputStream r;
        boolean print;

        Dumper(InputStream r, boolean print) {
            this.r = r;
            this.print = print;
        }

        @Override
        public void run() {
            int c;
            try {
                while ((c = r.read()) != -1) {
                    if (print) System.out.print((char)c);
                }
                r.close();
            } catch (IOException ex) {}
        }
    }

    public static void main(String[] args) throws Exception {
        Process p = Runtime.getRuntime().exec("java -jar C:\\Users\\millerti\\Documents\\NetBeansProjects\\Producer\\dist\\Producer.jar");
        new Dumper(p.getErrorStream(), false).run();
        new Dumper(p.getInputStream(), true).run();
        p.waitFor();
    }
}

预期行为:由于生产者刷新其输出并且消费者完全没有缓冲,消费者应该实时获得生产者的输出。

观察到的行为:只有当制作人完成时,消费者才会立即收到制作人的输出。

原帖

我正在与Windows应用程序上的其他开发人员合作。她编写了一个C程序(我们可以修改),它执行媒体转换并将进度消息(百分比)打印到stdout。我正在用Java编写图形前端,我希望我的程序能够捕获这些消息并更新GUI元素。

相关的C程序代码如下所示:

printf ("progress_msg: %d%%\n", currentProgress);
fflush(stdout);

在Java程序中,我使用Runtime.getRuntime().exec()来运行C程序并获取C程序的stdout和stderr的原始InputStream对象。 (stderr只是被另一个线程抛弃了。)我正在解析stdout流,寻找那些消息(好吧,我是,使用BufferedReader,但是现在,我正试图调试这个,所以我直接与低级别的InputStream字节源进行对话。)

问题在于,尽管Java端(我知道)没有缓冲,并且C程序中有fflush,但只有当C时才会出现stdout文本节目结束。这与我从命令提示符运行它不同,其中消息按预期逐渐出现。

很明显,有一些缓冲正在进行。 Java中的Process工具是否在做一些我不了解的缓冲?如果是这样,有没有办法将其关闭? Windows中的fflush(stdout)无法以预期的方式运行吗?什么是正确的Windows等价物?

感谢。

注意:我找到了两个相关的stackoverflow,但他们没有回答这个问题。特别是,我们可以修改C程序,没有行缓冲,我们正在进行适当的刷新(据我们所知)。请参阅I want realtime output of my Runtime.getRuntime().exec()Unbuffered subprocess stdout on windows

1 个答案:

答案 0 :(得分:1)

我的一个疯狂的猜测,但你说:

  

... 我正在用Java编写图形前端,我希望我的程序能够捕获这些消息并更新GUI元素。
  ......

     

在Java程序中,我使用Runtime.getRuntime()。exec()来运行C程序并获取C程序的stdout和stderr的原始InputStream对象。 ...我正在解析stdout流,正在寻找那些消息...

     

问题是虽然Java端没有缓冲(我知道)并且在C程序中有一个fflush,但只有当C程序完成时才会出现stdout文本。

我再次猜测,因为你遗漏了重要的细节,所有代码,但如果你的前端是一个Swing GUI,那么你所描述的症状表明你试图获得有关Swing的所有信息事件线程。如果这是一个Swing应用程序,解决方案是确保在后台线程(例如SwingWorker提供的线程)中读取Stream的输出,以确保缓冲您正在读取的数据,然后以线程安全的方式在GUI中显示它(再次使用SwingWorker,尤其是其发布/处理方法对)。

为了获得更好的帮助,请向我们提供更好的信息和代码,最好是我们可以运行,测试和改进的一小部分,实际上是最小的example program


修改
我的测试程序:

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Consumer {

   static class StreamGobbler implements Runnable {
      private String name;
      private boolean print;
      private Scanner scanner;

      public StreamGobbler(String name, InputStream inputStream, boolean print) {
         this.name = name;
         this.print = print;

         scanner = new Scanner(inputStream);
      }

      @Override
      public void run() {
         System.out.printf("in %s run method%n", name);
         while (scanner.hasNextLine()) {
            String line = scanner.nextLine();
            if (print) {
               String output = String.format("From %s: %s%n", name, line);
               System.out.printf(output);
            }
         }
         if (scanner != null) {
            scanner.close();
         }
      }
   }

   private static final String PRODUCER_PATH = "C:/Users/Pete/Documents/Fubar/Java/producer.jar";

   public static void main(String[] args) {
      List<String> command = new ArrayList<>();
      command.add("java.exe");
      command.add("-jar");
      command.add(PRODUCER_PATH);

      try {
         ProcessBuilder pBuilder = new ProcessBuilder(command);
         Process process = pBuilder.start(); 
         StreamGobbler errorGobbler = new StreamGobbler("Error Gobbler", process.getErrorStream(), true);
         StreamGobbler inputGobbler = new StreamGobbler("Input Gobbler", process.getInputStream(), true);

         new Thread(errorGobbler).start();
         new Thread(inputGobbler).start();
         process.waitFor();
      } catch (IOException | InterruptedException e) {
         e.printStackTrace();
      }
   }
}