来自java中交互式外部进程的输入和输出

时间:2011-11-20 10:07:14

标签: java unix process

我正在尝试使用流程构建器类从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类将进程添加到队列,它负责运行进程并启动读写线程。

2 个答案:

答案 0 :(得分:2)

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

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

解决方案是flush输出流。刷新输出流将当前保存在缓冲区中的任何数据发送到目标。要刷新输出流,请添加行

out.flush();

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

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

答案 1 :(得分:1)

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