使用Java程序关闭占用端口号的进程

问题描述

​ 在我日常编程中使用IDEA作为编译器,有时在关闭项目或者直接退出编译器时导致进程未关闭。下一次再次启动SpringBoot程序后,提示端口已被占用。
​ 闲暇时间通过查阅资料实现了通过java代码方式(批量)关闭占用进程的方式,记录一下。

解决方法

普通解决方法 :使用管理员身份打开Terminal(或cmd) cmd快捷键 win + r

# 列出所有的跟80相关的端口,有可能出现很多的情况 
netstat -ano | findstr 80
# 找到占用端口所在行最右侧的数为pid
taskkill /F /pid pid

进阶解决方法:使用Java程序 源码如下

package app.killServer;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class KillServer {
    private Set<?> ports;

    public static void main(String[] args) throws InterruptedException {
        // 输入逻辑
        System.out.println("请输入需要kill的进程端口号,如果有多个以逗号分隔:");
        System.out.print("Please input port here: ");
        Scanner sc = new Scanner(System.in);
        // 不接受空格
        String input = sc.next();
        sc.close();
        // 校验逻辑
        String[] split = input.split(",");
        Set<Integer> ports = new HashSet<>();
        for (String pidStr : split) {
            try {
                int pid = Integer.parseInt(pidStr);
                ports.add(pid);
            } catch (NumberFormatException e) {
                e.printStackTrace();
                //System.out.println(e.getMessage());
                System.out.println("输入的端口号格式不正确,请重新输入!");
                System.out.print("是否重新进行?(y/n): ");
                if ("y".equalsIgnoreCase(sc.next())) {
                    clearConsole();
                    main(args);
                } else {
                    System.out.println("程序结束~");
                    Thread.sleep(1000);
                    System.exit(0);
                }
            }
        }
        // 执行逻辑
        KillServer server = new KillServer();
        server.ports = ports;
        for (Integer pid : ports) {
            server.start(pid);
        }
        System.out.println("执行成功,程序即将推出...");
        Thread.sleep(2000);
        System.exit(0);
    }

    /**
     * 清空控制台
     */
    private static void clearConsole() {
        try {
            final String os = System.getProperty("os.name");
            if (os.contains("Windows")) {
                Runtime.getRuntime().exec("cls");
            } else {
                Runtime.getRuntime().exec("clear");
            }
        } catch (IOException e) {
            System.out.println("清除控制台出错");
        }
    }

    /**
     * 主要开始方法
     *
     * @param port 端口号
     */
    public void start(int port) {
        Runtime runtime = Runtime.getRuntime();
        try {
            // 查找进程号
            Process p = runtime.exec(String.format("cmd /c netstat -ano | findstr %d", port));
            InputStream is = p.getInputStream();
            List<String> read = read(is, "UTF-8");
            if (read.size() == 0) {
                System.out.printf("找不到 %d 端口号的进程,继续执行...%n", port);
            } else {
                System.out.printf("找到 %d 个进程,准备清理...%n", read.size());
                toSet(read);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取输入流的每一行代码
     *
     * @param in      InputStream
     * @param charset 字符集
     * @return 每一行数据的List
     */
    private List<String> read(InputStream in, String charset) throws IOException {
        List<String> data = new ArrayList<>();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset));
        String line;
        while ((line = reader.readLine()) != null) {
            boolean validPort = validPort(line);
            if (validPort) {
                data.add(line);
            }
        }
        reader.close();
        return data;
    }

    /**
     * 使用正则表达式执行是否为指定端口,findstr会将包含的所有端口都找出来 例如:80也会包含8083
     *
     * @param str 每一行
     * @return 端口是否有效
     */
    private boolean validPort(String str) {
        Pattern pattern = Pattern.compile("^ *[a-zA-Z]+ +\\S+");
        Matcher matcher = pattern.matcher(str);

        if (!matcher.find()) return false;
        String find = matcher.group();
        int lIndex = find.lastIndexOf(":");
        find = find.substring(lIndex + 1);
        int port;
        try {
            port = Integer.parseInt(find);
        } catch (NumberFormatException e) {
            System.out.println("查找到错误端口: " + find);
            return false;
        }

        return this.ports.contains(port);
    }

    /**
     * 将每一行进程信息的List转换为只有端口号的Set(去重)
     *
     * @param data 包含有全部端口信息的每一行数据list
     */
    private void toSet(List<String> data) {
        Set<Integer> pids = new HashSet<>();
        for (String line : data) {
            int lIndex = line.lastIndexOf(" ");
            String spid = line.substring(lIndex + 1).trim();
            int pid;
            try {
                pid = Integer.parseInt(spid);
                pids.add(pid);
            } catch (NumberFormatException e) {
                System.out.println("获取进程号错误: " + spid);
            }
            kill(pids);
        }
    }

    /**
     * 一次性kill所有端口
     *
     * @param pids 端口集合
     */
    private void kill(Set<Integer> pids) {
        for (Integer pid : pids) {
            try {
                Process process = Runtime.getRuntime().exec(String.format("taskkill /F /pid %d", pid));
                InputStream is = process.getInputStream();

                String txt = readTxt(is, "GBK");
                System.out.println(txt);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    private String readTxt(InputStream in, String charset) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset));
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        reader.close();
        return sb.toString();
    }

}

采纳方便点个赞,谢谢~

posted @ 2021-08-10 18:27  小甄的全栈梦  阅读(877)  评论(0编辑  收藏  举报