【问题标题】:Input and Output from Interactive external process in javajava中交互式外部进程的输入和输出
【发布时间】:2011-11-20 10:07:14
【问题描述】:

我正在尝试使用进程构建器类从 java 运行用 C 编写的交互式程序。 C 程序非常简单,它要求用户输入一个数字,然后将相同的数字输出给用户。 当我尝试运行 java 程序时它挂起,请让我知道问题所在。

Java 程序

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package testapp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author pradeep
 */
public class TestApp {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException, InterruptedException {

        Process process = new ProcessBuilder("/home/pradeep/a.out").start();
        final InputStream in = process.getInputStream();
        final OutputStream out = process.getOutputStream();
        final BufferedReader br = new BufferedReader(new InputStreamReader(in));
        //Thread.currentThread().sleep(5000);
        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    int ch;
                    String line;
                    System.out.println("Read started");
                    do {
                        //line=br.readLine();
                        ch = in.read();
                        System.out.print((char)ch);
                    }while(ch!=-1);
                    System.out.println("Read ended");

                } catch (IOException ex) {
                    Logger.getLogger(TestApp.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

        }).start();

        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    int i=0;
                    System.out.println("write started");
                        out.write(((i++)+"\n").getBytes());

                    System.out.println("write completed");
                } catch (IOException ex) {
                    Logger.getLogger(TestApp.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

        }).start();

        int result = process.waitFor();
        //out.close();
        //in.close();
        System.out.println("result:"+result);
    }
}

C 程序

#include<stdio.h>

int main() {
int number;

int i=10;

printf("Enter number:\n"); scanf("%d",&number); printf("Entered
number:%d\n",number);

return 0;
}

当我像下面这样修改java程序中的写线程时,我得到了输出。

new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    int i=0;
                    System.out.println("write started");
                    while(i<2000) {
                        //System.out.println("W->"+i);
                        out.write(((i++)+"\n").getBytes());
                    }

                    System.out.println("write completed");
                } catch (IOException ex) {
                    Logger.getLogger(TestApp.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

        }).start();

下面是框架代码,

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.amphisoft.framework;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author pradeep
 */
public class ProcessManager {

    private static ArrayBlockingQueue<Task> taskQueue = new ArrayBlockingQueue<Task>(10);
    private static Map<Integer,ProcessHolder> processMap = new HashMap<Integer,ProcessHolder>();

    private static ProcessManager instance;
    Integer id = 0;

    Log log = LogFactory.getLog(ProcessManager.class);

    private ProcessManager() {

        new Thread() {

            @Override
            public void run() {
                try {
                    Task task;

                    while((task = taskQueue.take()) != null) {
                        Process process = task.processBuilder.start();
                        ProcessHolder processHolder = new ProcessHolder(process,process.getInputStream(),process.getOutputStream());
                        if(task.hasInput) {
                            WriteThread writeThread = new WriteThread(processHolder);
                            writeThread.start();
                        }
                        if(task.hasOutput) {
                            ReadThread readThread = new ReadThread(processHolder);
                            readThread.start();
                        }
                        processMap.put(task.id,processHolder);
                        System.out.println("waiting for process");
                        process.waitFor();
                        System.out.println("process completed");
                    }
                }catch(Exception e) {
                    e.printStackTrace();
                }
            }

        }.start();

    }

    public static ProcessManager getInstance() {
        if(instance == null) {
            instance = new ProcessManager();
        }

        return instance;
    }

    public Integer addTask(Task task) {
        id++;
        task.id = id;
        taskQueue.add(task);
        return task.id;

    }

    public ProcessHolder getProcessHolder(Integer id) {
        log.info("Get ProcessHolder:"+id);
        log.info("Process Map:"+processMap);

        return processMap.get(id);
    }

}

ProcessHolder.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.amphisoft.framework;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PipedReader;
import java.io.PipedWriter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 *
 * @author pradeep
 */
public class ProcessHolder {
    private Process process;
    private PipedReader reader;
    private PipedWriter readerInput;

    private PipedWriter writer;
    private PipedReader writerOutput;

    private BufferedReader in;
    private BufferedWriter out;

    Log log = LogFactory.getLog(ProcessHolder.class);

    public ProcessHolder(Process process, InputStream in, OutputStream out) throws IOException {
        this.process = process;
        this.in = new BufferedReader(new InputStreamReader(in));
        this.out = new BufferedWriter(new OutputStreamWriter(out));

        readerInput = new PipedWriter();
        reader = new PipedReader(readerInput);

        writer = new PipedWriter();
        writerOutput = new PipedReader(writer);
    }

    public void readToPipe() throws IOException, InterruptedException {

        String line = "";
        log.info("Inside readToPipe");
        int ch;
        while((ch = in.read()) != -1) {
            log.info(ch);
            readerInput.write(ch);
            readerInput.flush();
        }


        log.info("Closing the read Pipe");
        in.close();
        readerInput.close();
    }

    public void writeFromPipe() throws IOException, InterruptedException {
        char[] buffer = new char[512];
        int ch;
        log.info("Inside writeFromPipe to write");
        while((ch = writerOutput.read()) != -1) {
            log.info(ch);
            out.write(ch);
            out.flush();
        }

        log.info("Closing the write Pipe");
        out.close();
        writerOutput.close();
    }

    public PipedReader getReader() {
        return reader;
    } 

    public PipedWriter getWriter() {
        return writer;
    }
}

Task.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.amphisoft.framework;

/**
 *
 * @author pradeep
 */
public class Task {
    public ProcessBuilder processBuilder;
    public Integer id;
    public boolean hasInput;
    public boolean hasOutput;
}

WriteThread.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.amphisoft.framework;

/**
 *
 * @author pradeep
 */
public class WriteThread extends Thread{

    ProcessHolder processHolder;

    public WriteThread(ProcessHolder processHolder) {
        this.processHolder = processHolder;
    }

    public void run() {
        try {
            processHolder.writeFromPipe();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

ReadThread.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.amphisoft.framework;

/**
 *
 * @author pradeep
 */
public class ReadThread extends Thread{

    public ProcessHolder processHolder;

    public ReadThread(ProcessHolder processHolder) {
        this.processHolder = processHolder;
    }

    public void run() {
        try {
            processHolder.readToPipe();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

应用程序使用 processmanager 类将进程添加到队列中,它负责运行进程并启动读写线程。

【问题讨论】:

    标签: java unix process


    【解决方案1】:

    问题是进程的输入和输出流被缓冲了。

    当您向进程写入单行时,单行不足以填满缓冲区。输出流正在等待更多数据填充缓冲区,然后再将其全部写入进程。在您的第一个示例中,您的 Java 程序挂起,因为 C 程序正在等待从 Java 程序发送的输入行,但由于缓冲,这永远不会到来。

    解决办法是flush输出流。刷新输出流会将当前保存在缓冲区中的任何数据发送到目的地。要刷新输出流,请添加以下行

    out.flush();
    

    在您的第一个示例中的 out.write 行之后。

    在第二个示例中,您将大量数字写入输出流。这显然足以填充缓冲区并导致输出流将数据发送到 C 程序。

    【讨论】:

    • 感谢您的回复,在下面的代码中添加 out.flush() 后解决了我的问题。我们创建了一个类似于上面给出的原型的小框架,用于执行外部程序并将进程的输入和输出读取到管道流中。然后应用程序可以从管道流中读取/写入数据。我使用 in.read() 逐个字符地读取进程输入流,并在 while 循环中写入管道流。但是线程挂在 read() 方法中。因为我除了看到第一行“输入数字:”应该被写入管道流但从未发生过。
    • 我面临的类似问题在下面的线程bytes.com/topic/java/answers/…中进行了讨论
    • @user878277:没有看到你的“框架”的代码,我只能猜测发生了什么。也许 C 程序也在缓冲它的输出?
    • 我获取了您的代码并添加了另一个类,我可以运行该类来使用那个“简单”C 程序来驱动您的“框架”,但我无法重现您的问题。
    • 我不敢相信我必须查看这么多 Q/A 才能找到这个答案。我使用的所有教程都不起作用,我只需要flush()。 +1
    【解决方案2】:

    由于您在第一个代码示例中只写了一个数字(没有 while 循环,只写“0\n”),您可以flush 或关闭输出流。

    【讨论】:

    • 刷新或关闭 OutputStream 无效。
    • @Tudor:我获取了 OP 的代码,添加了对 out.flush() 的调用,它按预期工作。至少对我来说,冲洗确实有效果。
    • 对不起,我错了... OutputStream 是一个抽象类,所以 Socket.getOutputStream() 返回的是一个子类,它的 flush 方法可能会做它应该做的事情。 +1
    猜你喜欢
    • 2013-11-21
    • 1970-01-01
    • 2012-05-19
    • 1970-01-01
    • 1970-01-01
    • 2013-05-06
    • 1970-01-01
    • 2013-11-12
    • 2011-09-13
    相关资源
    最近更新 更多