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

标签 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;
    }
}

任务.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 程序挂起,因为 C 程序正在等待从 Java 程序发送的输入行,但由于缓冲,这永远不会到来。

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

out.flush();

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

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

关于java - java中交互式外部进程的输入和输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8200804/

相关文章:

java - 检索 ProcessBuilder 的输出

java - OpenJDK 11.0.2 的 Spring Boot 2.0 请求映射问题

java - 使用 JPA/EJB3 进行批量插入

java - 从单独的类引用 Jframe

linux - 如何查找特定格式的数字并过滤掉特定范围的值?

c - 辅助进程完成时终止 Main

java - 计算器不能有小数

linux - 在 ping 中使用 -m 选项

c - GNU getopt() 的 -W 选项有什么用?

c++ - 如何获取当前流程的工作对象(如果有)?