Bootstrap

c#杀死对应端口的TCP进程

在运行SpringBoot程序时,有时会因为对应端口被占用导致程序运行失败,打开cmd杀死对应的进程太过于麻烦,所以写了该程序,java不够熟练,所以选择了熟练的c#语言来写该程序,使用的环境为:.net 6。

该程序功能实现是这样的:通过调用cmd程序,并根据用户输入的对应的IP地址或者是端口号杀死对应的进程。该程序使用到的cmd命令适用于win11,老系统是否支持就不太清楚了。

第一步,先调用cmd程序,并执行对应的命令,代码如下:(该代码从网上抄的,略加修改,该代码涉及到动态类型dynamic和可选参数,不懂的同学可以百度查询一下)

public static dynamic RunCmd(string cmd,bool isReturnStreamReader=false)
        {
            Process pro = new Process();
            pro.StartInfo.FileName = "cmd.exe";

            pro.StartInfo.CreateNoWindow = true;         // 不创建新窗口    
            pro.StartInfo.UseShellExecute = false;       //不启用shell启动进程  
            pro.StartInfo.RedirectStandardInput = true;  // 重定向输入    
            pro.StartInfo.RedirectStandardOutput = true; // 重定向标准输出    
            pro.StartInfo.RedirectStandardError = true;
            pro.StartInfo.StandardErrorEncoding = System.Text.UTF8Encoding.UTF8;
            pro.StartInfo.StandardOutputEncoding = System.Text.UTF8Encoding.UTF8;
            // 重定向错误输出  
           // pro.StartInfo.WorkingDirectory = path;
            pro.Start();//开启cmd
            pro.StandardInput.WriteLine(cmd);
            pro.StandardInput.AutoFlush = true;
            pro.StandardInput.WriteLine("exit"); //若是运行时间短可加入此命令

            dynamic output;
            if (isReturnStreamReader)
            {
                output = pro.StandardOutput;
            }
            else
            {
                output = pro.StandardOutput.ReadToEnd();
            }

            pro.WaitForExit();//若运行时间长,使用这个,等待程序执行完退出进程
            pro.Close();
            return output;
        }

之后我们调用cmd获取对应ip或者端口号的所有进程信息,使用命令"netstat -aon|findstr 地址或者端口号"即可获取,获取PID代码如下:

        public static List<string> GetPidOfAddress(string address)
        {
            //用来保存所有对应地址的端口号数据
            List<string> valueList = new List<string>(); 
            //获取返回的StreamReader数据
            StreamReader sr = RunCmd($"netstat -aon|findstr {address}", true);
            //读取StreamReader的每一行数据
            while (!sr.EndOfStream)
            {
                string value = sr.ReadLine();
                //查看当前读取的行中内容是否包含TCP字符
                if (!string.IsNullOrEmpty(value) && value.Contains("TCP"))
                {
                    //只获取状态为 LISTENING 的数据
                    string[] ss = value.Split("LISTENING");
                    //获取端口号数据信息
                    if (ss.Length >= 2)
                    {
                        string port = ss[ss.Length - 1].Trim();
                        //重复的端口号不加入结果列表中
                        if (valueList.FirstOrDefault(a => a == port) == null)
                            valueList.Add(port);
                    }
                }
            }
            return valueList;
        }

我们可以从获取到的进程信息中获取到对应的pid信息,并使用"taskkill /F /pid 进程的PID"命令杀死对应进程,代码如下:

        /// <summary>
        /// 根据PID杀死对应进程
        /// </summary>
        /// <param name="pid"></param>
        /// <returns></returns>
        public static string KillTask(string pid)
        {
            return RunCmd($"taskkill /F /pid {pid}");
        }

以上代码我均放在CmdHelper类中,在mian函数中调用如下:

    Console.WriteLine("请输入对应的地址或者端口号");
    string addressOrPort = Console.ReadLine();
    //如果用户输入空则将目标设置为8888端口,即0.0.0.0:8888
    if (string.IsNullOrEmpty(addressOrPort))
    {
        addressOrPort = "0.0.0.0:8888";
    }
    Console.WriteLine($"当前你要杀死的进程的地址或端口号为:{addressOrPort}");

    Console.WriteLine("\n查询到的进程如下:");
    Console.WriteLine(CmdHelper.ListTcp(addressOrPort));

    //Console.WriteLine("\n当前要杀死的进程的PID为:");
    List<string> pidList = CmdHelper.GetPidOfAddress(addressOrPort);
    foreach (string item in pidList)
    {
        Console.WriteLine($"{item}");
        CmdHelper.KillTask(item);
        Console.WriteLine("已经杀死进程" + item);
    }
    Console.WriteLine();

    Console.WriteLine("按任意键退出程序");
    Console.ReadKey();

运行完成之后,对应端口的spring boot程序也结束了运行,效果还行,以后不用费劲的打开cmd慢慢整了。提示:pid为0的进程好像是系统进程是不能杀死的。

;