C#判断一个端口是不是被占用以及返回一个空闲端口 您所在的位置:网站首页 怎么判断当前socket端口已经开启了 C#判断一个端口是不是被占用以及返回一个空闲端口

C#判断一个端口是不是被占用以及返回一个空闲端口

2024-01-06 14:15| 来源: 网络整理| 查看: 265

一.引言

在最近的工作当中,用到了 Socket 通信,然后要给 Socket 服务器端的监听获取一个空闲的本地监听端口。

对于这个获取方法要满足如下几点的要求:

 这个端口不能是别的程序所使用的端口;  这个获取要支持异步,即多个线程同时获取不会出现返回多个相同的空闲端口(即线程安全);  这端口要有效的遍历一个区域内的端口,直到返回一个可用的空闲端口;  二.实现方法

网上的实现方法主要有两种:

1. 使用 .NET 提供的 IPGlobaProperties.GetIPGlobaProperties() 来获得一个 IPGlobaProperties 对象,然后通过它的成员函数  GetActiveTcpListeners()、GetActiveUdpListeners() 以及 GetActiveTcpConnections() 来获得被连接或者被监听所使用了的端口,进而刷选出空闲的端口:

//获取本地计算机的网络连接和通信统计数据的信息 IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); //返回本地计算机上的所有Tcp监听程序 IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners(); //返回本地计算机上的所有UDP监听程序 IPEndPoint[] ipsUDP = ipProperties.GetActiveUdpListeners(); //返回本地计算机上的Internet协议版本4(IPV4 传输控制协议(TCP)连接的信息 TcpConnectionInformation[] tcpConnInfoArray = ipProperties.GetActiveTcpConnections();

2. 使用 Process 创建一个命令行进程,执行命令 " netstat -an " 来获得所有的已经被使用的端口,我们仅仅通过 cmd 窗体输入这个命令的输出如下:

我们通过匹配 " :端口号 " 是不是在上面返回的数据中就可以很容易的知道端口是不是被占用。

 

经过测试之后发现,使用第一种方法有时候并不能检索到部分被使用了的端口,所以最后还是使用了第一种和第二种混合的检测方案。

三.程序代码

通过第一种和第二种方法各查询一次并缓存,在本次查询中使用这个缓存(为了平衡效率与 " 在查找的时候被端口被占用 " 的问题)。于此同时,我们通过 lock 来避免异步问题,并且对于前后两次获取,如果前一个端口被获取到,那么我们之后的端口就从前一个的后面那个开始做查询。

下面是程序的核心代码:

public static class IPAndPortHelper { #region 成员字段 /// /// 同步锁 /// 用来在获得端口的时候同步两个线程 /// private static object inner_asyncObject = new object(); /// /// 开始的端口号 /// private static int inner_startPort = 50001; #endregion #region 获得本机所使用的端口 /// /// 使用 IPGlobalProperties 对象获得本机使用的端口 /// /// 本机使用的端口列表 private static List GetPortIsInOccupiedState() { List retList = new List(); //遍历所有使用的端口,是不是与当前的端口有匹配 try { //获取本地计算机的网络连接和通信统计数据的信息 IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); //返回本地计算机上的所有Tcp监听程序 IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners(); //返回本地计算机上的所有UDP监听程序 IPEndPoint[] ipsUDP = ipProperties.GetActiveUdpListeners(); //返回本地计算机上的Internet协议版本4(IPV4 传输控制协议(TCP)连接的信息 TcpConnectionInformation[] tcpConnInfoArray = ipProperties.GetActiveTcpConnections(); //将使用的端口加入 retList.AddRange(ipEndPoints.Select(m => m.Port)); retList.AddRange(ipsUDP.Select(m => m.Port)); retList.AddRange(tcpConnInfoArray.Select(m => m.LocalEndPoint.Port)); retList.Distinct();//去重 } catch(Exception ex)//直接抛出异常 { throw ex; } return retList; } /// /// 使用 NetStat 命令获得端口的字符串 /// /// 端口的字符串 private static string GetPortIsInOccupiedStateByNetStat() { string output = string.Empty; try { using (Process process = new Process()) { process.StartInfo = new ProcessStartInfo("netstat", "-an"); process.StartInfo.CreateNoWindow = true; process.StartInfo.UseShellExecute = false; process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; process.StartInfo.RedirectStandardOutput = true; process.Start(); output = process.StandardOutput.ReadToEnd().ToLower(); } } catch(Exception ex) { throw ex; } return output; } #endregion #region 获得一个当前没有被使用过的端口号 /// /// 获得一个当前没有被使用过的端口号 /// /// 当前没有被使用过的端口号 public static int GetUnusedPort() { /* * 在端口获取的时候防止两个进程同时获得一个一样的端口号 * 在一个线程获得一个端口号的时候,下一个线程获取会从上一个线程获取的端口号+1开始查询 */ lock (inner_asyncObject)//线程安全 { List portList = GetPortIsInOccupiedState(); string portString = GetPortIsInOccupiedStateByNetStat(); for (int i = inner_startPort; i < 60000; i++) { if (portString.IndexOf(":" + inner_startPort) < 0 && !portList.Contains(inner_startPort)) { //记录一下 下次的端口查询从 inner_startPort+1 开始 inner_startPort = i + 1; return i; } } //如果获取不到 return -1; } } #endregion }

 

测试代码:

Console.WriteLine(IPAndPortHelper.GetUnusedPort()); Console.WriteLine(IPAndPortHelper.GetUnusedPort());

测试结果图:

四.工程代码下载

下载地址 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有