Java与外部进程交互从精通到陌生

1.什么是进程

现代操作系统运行一个程序的时候,会为其创建一个进程,例如启动一个IDEA,操作系统就会创建一个IDEA进程

2.如何与进程交互

Process

在这里插入图片描述
注意:如果使用waitFor() 或者长时间不去读取流,由于部分原生平台只为标准输入输出流提供有限的缓冲区大小,未能及时写入子进程的输入流或读取输出流可能会导致子进程阻塞,甚至死锁。

创建方式
1.使用Runtime
          Runtime runtime = Runtime.getRuntime();
            try {
           Process  exec = runtime.exec(cmdStr);
            } catch (IOException e) {
                e.printStackTrace();
            }
2.使用ProcessBuilder
        Process p = null;
        try{
            ProcessBuilder processBuilder = new ProcessBuilder("ipconfig", "/all");
            processBuilder.
            p = processBuilder.start(); //执行ipconfig/all命令

        }catch(IOException e){
            e.printStackTrace();
        }

Runtime

Java 注释中是这样介绍的:
每个Java应用程序都有一个类运行时实例,它允许应用程序与运行应用程序的环境交互。可以从getRuntime方法获取当前运行时。

常用方法介绍

        Runtime runtime = Runtime.getRuntime();
        //可用处理器数
        int processors = runtime.availableProcessors();
        //返回jvm中空闲的内存量 单位字节
        long freeMemory = runtime.freeMemory();
        System.out.println(freeMemory);
        //jvm可使用的最大内存
        long maxMemory = runtime.maxMemory();
        //jvm总内存
        long totalMemory = runtime.totalMemory();

        try {
            //执行字符串命令
            runtime.exec("notepad");
        } catch (IOException e) {
            e.printStackTrace();
        }
        // jvm关闭时的钩子(就是在jvm关闭的时候 会启动addShutdownHook方法添加的线程)
        runtime.addShutdownHook(new Thread(() -> {
            System.out.println("jvm退出");
        }));
        //通知jvm垃圾回收 但只是通知
        runtime.gc();
        long nowFreeMemory = runtime.freeMemory();
        System.out.println(nowFreeMemory);
        runtime.exit(1);
        //exit 下面语句不会执行 包括finally{}
        System.out.println("processors=" + processors + ", freeMemory=" + freeMemory + ", maxMemory=" + maxMemory + ", totalMemory=" + totalMemory);

应用程序无法创建自己的此类实例。

ProcessBuilder

不想敲了看,这篇文章总结的可以
注意 使用processBuilder.command(cmds) 每个命令不要包含空格,以免发生意外
如下:

 ArrayList<String> cmds = new ArrayList<>();
            cmds.add("curl");
            cmds.add("-X");
            cmds.add("POST");

中文乱码解决

因为我们与不同的进程交互,每个进程像我们返回的字符集编码是不确定的
所以用一些较大的字符集来接收,因为他们可以兼容一些字符集,这里不做过多描述,有兴趣的可以看这篇文章
Java中的乱码
可以使用GBK或者UTF-8来接收解决中文乱码的问题
但是如果外部进程是用UTF-8编码但是我们用GBK接收 ,在GBK中 一个中文2字节,UTF-8是3字节,所有肯定乱码

//UTF-8编码但是我们用GBK接收 
//cannot connect to 127.0.0.1:5555: 由于目标计算机积极拒绝,无法连接。 (10061)
 String s = "cannot connect to 127.0.0.1:5555: 鐢变簬鐩\uE1BD爣璁$畻鏈虹Н鏋佹嫆缁濓紝鏃犳硶杩炴帴銆� (10061)";
 byte[] gbks = s.getBytes("GBK");
 //GBK转UTF-8
   System.out.println(new String(gbks, StandardCharsets.UTF_8));   

输出

cannot connect to 127.0.0.1:5555: 由于目标计算机积极拒绝,无法连接�? (10061)

为什么。变成了�? 因为GBK中文2字节,UTF-8中文3字节,在用GBK接收的时候如果字节数不是偶数那么最后一个字节GBK识别不出来,就会用半角Ascii字符的 “?” 代替,所以数据就被破坏,所以接收采用单字节的ISO-8859-1数据就不会被破坏关于GBK和UTF-8互转乱码的可以看这篇文章介绍的很不错

总结
1.采用ISO-8859-1接收
2.判断是否是utf-8

 public static String getUtf8String(String str) {
        if (str != null && str.length() > 0) {
            try {
                byte[] bytes = str.getBytes(StandardCharsets.ISO_8859_1);
                String encode = "";
                boolean pdUtf = false;
                pdUtf = validUtf8(bytes);
                if (pdUtf) {
                    encode = String.valueOf(StandardCharsets.UTF_8);
                } else {
                    encode = "GBK";
                }
                str = new String(str.getBytes(StandardCharsets.ISO_8859_1), encode);

            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        return str;
    }

    public static boolean validUtf8(byte[] data) {
        int i = 0;
        int count = 0;
        while (i < data.length) {
            int v = data[i];
            if (count == 0) {
                if ((v & 240) == 240 && (v & 248) == 240) {
                    count = 3;
                } else if (((v & 224) == 224) && (v & 240) == 224) {
                    count = 2;
                } else if ((v & 192) == 192 && (v & 224) == 192) {
                    count = 1;
                } else if ((v | 127) == 127) {
                    count = 0;
                } else {
                    return false;
                }
            } else {
                if ((v & 128) == 128 && (v & 192) == 128) {
                    count--;
                } else {
                    return false;
                }
            }

            i++;
        }

        return count == 0;
    }

模拟与同一个cmd交互

  • cmd /c 是执行完命令后关闭命令窗口。

  • cmd /k 是执行完命令后不关闭命令窗口。

  • cmd /c start 会打开一个新窗口后执行指令,原窗口会关闭。

  • cmd /k start 会打开一个新窗口后执行指令,原窗口不会关闭。

 /**
     * cmd /c  是执行完命令后关闭命令窗口
     */
    private static final String CLOSE_AFTER_EXECUTION = "cmd /c ";

    /**
     * 是执行完命令后不关闭命令窗口
     */
    private static final String DO_NOT_CLOSE_AFTER_EXECUTION = "cmd /k ";

    /**
     * 会打开一个新窗口后执行命令,原窗口会关闭
     */
    private static final String OPEN_NEW_WINDOWS_EXECUTION_OLD_WINDOWS_CLOSE = "cmd /c start";

    /**
     * 会打开一个新窗口后执行命令,原窗口不会关闭
     */
    private static final String OPEN_NEW_WINDOWS_EXECUTION_OLD_WINDOWS_NOT_CLOSE = "cmd /k start";


    private static final String CD = "cd ";

    public static final String WRAP = System.getProperty("line.separator");


    /**
     * cmd  输入流
     */
    public static BufferedReader bufferedReader;
    /**
     * cmd 输出流
     */
    private static BufferedWriter bufferedWriter;

    /**
     * 执行完命令后不关闭命令窗口的进程
     */
    private static Process exec = null;

    private static Thread readThread;
    private static AtomicBoolean first = new AtomicBoolean(true);
    private static volatile String currentlyExecutedCommand;

    /**
     * 执行完命令后关闭命令窗口
     *
     * @param cmdStrBuf 命令
     * @return
     */
    public static String closeAfterExecution(StringBuffer cmdStrBuf) {
        cmdStrBuf.insert(0, CLOSE_AFTER_EXECUTION);
        return executeCmd(cmdStrBuf.toString());
    }

    /**
     * 执行完命令后不关闭命令窗口
     *
     * @param cmdStrBuf 命令
     * @return
     */
    public static void notCloseAfterExecution(StringBuffer cmdStrBuf) {
        cmdStrBuf.insert(0, DO_NOT_CLOSE_AFTER_EXECUTION);
        appendCmd(cmdStrBuf.toString());
    }

    public static void notCloseAfterExecution(String cmdStr) {
        StringBuffer cmdStrBuf = new StringBuffer(cmdStr);
        cmdStrBuf.insert(0, DO_NOT_CLOSE_AFTER_EXECUTION);
        appendCmd(cmdStrBuf.toString());
    }

   /* public static String openNewWindowsExecutionOldWindowsClose(StringBuffer cmdStrBuf) {
        cmdStrBuf.insert(0, OPEN_NEW_WINDOWS_EXECUTION_OLD_WINDOWS_NOT_CLOSE);
        return executeCmd(cmdStrBuf.toString());
    }*/

    public static void cdPath(String path) {
        StringBuffer stringBuffer = new StringBuffer(CD);
        stringBuffer.append(path);
        notCloseAfterExecution(stringBuffer);
    }


    public static void main(String[] args) throws Exception {


     /*   String ss = "cannot connect to 127.0.0.1:5555: 由于目标计算机积极拒绝,无法连接。 (10061)";
        byte[] bytes = ss.getBytes(StandardCharsets.UTF_8);

        String s = "cannot connect to 127.0.0.1:5555: 鐢变簬鐩\uE1BD爣璁$畻鏈虹Н鏋佹嫆缁濓紝鏃犳硶杩炴帴銆� (10061)";
        byte[] gbks = s.getBytes("GBK");

        Charset gbk = Charset.forName("GBK");
        ByteBuffer encode = gbk.encode(s);
        Charset utf = Charset.forName("UTF-8");
        CharBuffer decode = utf.decode(encode);
        for (int i = 0; i < decode.length(); i++) {
            System.out.print(decode.get(i));
        }


        System.out.println(encode.toString());
        System.out.println(new String(bytes, "GBK"));
        System.out.println(new String(new String(gbks, StandardCharsets.ISO_8859_1).getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
        System.out.println(new String(gbks, StandardCharsets.UTF_8));*/
    }

    private static Object outputObject;
    private static String methodName;

    public static void setOutputAddress(Object obj, String methodName) {
        CmdUtil.methodName = methodName;
        CmdUtil.outputObject = obj;
    }

    public static String getUtf8String(String str) {
        if (str != null && str.length() > 0) {
            try {
                byte[] bytes = str.getBytes(StandardCharsets.ISO_8859_1);
                String encode = "";
                boolean pdUtf = false;
                pdUtf = validUtf8(bytes);
                if (pdUtf) {
                    encode = String.valueOf(StandardCharsets.UTF_8);
                } else {
                    encode = "GBK";
                }
                str = new String(str.getBytes(StandardCharsets.ISO_8859_1), encode);

            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        return str;
    }

    public static boolean validUtf8(byte[] data) {
        int i = 0;
        int count = 0;
        while (i < data.length) {
            int v = data[i];
            if (count == 0) {
                if ((v & 240) == 240 && (v & 248) == 240) {
                    count = 3;
                } else if (((v & 224) == 224) && (v & 240) == 224) {
                    count = 2;
                } else if ((v & 192) == 192 && (v & 224) == 192) {
                    count = 1;
                } else if ((v | 127) == 127) {
                    count = 0;
                } else {
                    return false;
                }
            } else {
                if ((v & 128) == 128 && (v & 192) == 128) {
                    count--;
                } else {
                    return false;
                }
            }

            i++;
        }

        return count == 0;
    }


    /**
     * 输出目的地
     *
     * @param String 输出内容
     */
    private static void outputDestination(String content) {
        if (outputObject == null) {
            throw new NullPointerException("outputObject is null");
        }

        if (methodName == null) {
            throw new NullPointerException("methodName is null");
        }
        Class<?> aClass = outputObject.getClass();
        Method method = null;
        try {
            method = aClass.getMethod(methodName, String.class);
            method.invoke(outputObject, CmdUtil.getUtf8String(content));
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private static void processOutput(String cmdStr) {
        first.set(true);
        System.out.println("out");
        currentlyExecutedCommand = cmdStr;
        if (readThread == null) {
            readThread = new Thread(() -> {
                BufferedReader bufferedReader = CmdUtil.bufferedReader;

                try {
                    String text = bufferedReader.readLine();
                    while (text != null) {
                        if (text.equals("")) {
                            text = bufferedReader.readLine();
                            continue;
                        }
                        StringBuilder outStr = new StringBuilder();
                        if (first.get()) {
                            outStr.append(CmdUtil.WRAP).append(new Date()).append("--->").append(currentlyExecutedCommand).append(text).append(CmdUtil.WRAP);
                            first.set(false);
                        } else {
                            outStr.append(text).append(CmdUtil.WRAP);
                        }
                        outputDestination(outStr.toString());
                        text = bufferedReader.readLine();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }, "readThread");
            readThread.start();
        }
    }

    private static void appendCmd(String cmdStr) {
        if (!cmdStr.endsWith(WRAP)) {
            cmdStr = cmdStr + WRAP;
        }
        if (Objects.isNull(exec)) {
            Runtime runtime = Runtime.getRuntime();
            try {
                exec = runtime.exec(cmdStr);
                if (bufferedWriter == null) {
                    bufferedWriter = new BufferedWriter(new OutputStreamWriter(exec.getOutputStream(), "GBK"));
                }
                if (bufferedReader == null) {
                    SequenceInputStream sis = new SequenceInputStream(exec.getInputStream(), exec.getErrorStream());
                    bufferedReader = new BufferedReader(new InputStreamReader(sis, StandardCharsets.ISO_8859_1));
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            try {
                bufferedWriter.write(cmdStr);
                bufferedWriter.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        processOutput(cmdStr);
    }


 }

欢迎关注公众号
在这里插入图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值