C#判断一个端口是不是被占用以及返回一个空闲端口
标签:协议 rop exe 相同 工程 bsp The size cal
一.引言
在最近的工作当中,用到了 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 Listint> GetPortIsInOccupiedState()
{
Listint> retList = new Listint>();
//遍历所有使用的端口,是不是与当前的端口有匹配
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)//线程安全
{
Listint> 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());
测试结果图:
四.工程代码下载
下载地址
C#判断一个端口是不是被占用以及返回一个空闲端口
标签:协议 rop exe 相同 工程 bsp The size cal
原文地址:https://www.cnblogs.com/Jeffrey-Chou/p/12375743.html
评论