跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

C#怎么判断某个端口是否被占用_C#如何检查Socket监听状态【代码】

最直接、兼容性最好的端口占用判断方式是用TcpClient.ConnectAsync模拟连接:连通说明有服务监听,拒绝连接(ConnectionRefused)大概率未监听,超时则无法确定;需设短超时(如500ms)并匹配测试地址(如127.0.0.1或0.0.0.0)。 用
TcpClient
尝试连接来判断端口是否被占用 最直接、兼容性最好的方式不是查进程或监听列表,而是模拟一次连接——如果连得上,说明端口正在被监听(大概率被占用);连不上,不一定空闲(可能防火墙拦截、目标未开监听、跨网段不可达)。注意:这测的是“是否有服务在监听”,不是“是否被某个进程独占”。 常见错误现象:
SocketException
报错 “No connection could be made because the target machine actively refused it” 表示端口有监听但拒绝连接(比如只接受本地连接),而 “A connection attempt failed...” 更可能是端口根本没监听或网络不通。 仅适用于 TCP 端口;UDP 需用
UdpClient
发送探测包,但无可靠响应机制,不推荐用于判断占用 连接超时必须设短(如 500ms),否则阻塞主线程;建议用
ConnectAsync
+
CancellationToken
本地回环地址(
"127.0.0.1"
)和实际监听地址(如
"0.0.0.0"
或具体 IP)行为可能不同,测试时需匹配目标场景 示例:
var client = new TcpClient(); try { await client.ConnectAsync("127.0.0.1", 8080, TimeSpan.FromMilliseconds(500)); Console.WriteLine("端口 8080 正在被监听"); } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionRefused) { Console.WriteLine("端口 8080 未监听(大概率空闲)"); } catch (OperationCanceledException) { Console.WriteLine("连接超时 → 无法确定,可能被占用也可能被拦截"); } finally { client.Close(); }
用
IPGlobalProperties.GetActiveTcpListeners()
查本机监听地址 这个方法能拿到当前系统所有 TCP 监听端口的
IPAddress:Port
列表,适合做“本机自查”,不依赖网络连通性,但只能看到本机进程开启的监听,且需要管理员权限才能看到其他用户进程的监听项(Windows 上尤其明显)。 容易踩的坑:
0.0.0.0:8080
和
127.0.0.1:8080
是两个独立条目;前者表示监听所有接口,后者仅限本地;若程序绑定
0.0.0.0
,检查
127.0.0.1
会漏判。 返回结果不含进程名或 PID,无法直接知道谁占的 —— 如需定位,得配合
System.Diagnostics.Process
扫描,但跨用户/权限受限时失败率高 .NET 5+ 支持
GetActiveTcpListeners()
,旧版 .NET Framework 可用,但 .NET Core 3.1 及以前需用
IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()
IPv6 地址(如
::1
)和 IPv4 地址分开处理,别漏掉
IPAddress.IPv6Any
对应的监听 示例(检查是否监听 8080): C知道 CSDN推出的一款AI技术问答工具 下载
var listeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners(); bool isListening = listeners.Any(x => x.Port == 8080 && (x.Address.Equals(IPAddress.Any) || x.Address.Equals(IPAddress.Loopback) || x.Address.Equals(IPAddress.IPv6Any) || x.Address.Equals(IPAddress.IPv6Loopback));
为什么不用
netstat
或
Get-NetTCPConnection
? 调用外部命令看似简单,但实际落地问题多:权限控制难(普通用户无法看到系统进程)、解析输出易出错(不同 Windows 版本字段顺序/空格不一致)、跨平台失效(Linux/macOS 命令不同)、启动进程有开销。PowerShell 的
Get-NetTCPConnection
虽原生,但需加载模块、非默认执行策略下常被禁用,且返回对象结构在不同系统版本中不稳定。 性能与可靠性权衡:一次
TcpClient
连接尝试耗时毫秒级;而启动
netstat
进程 + 解析文本平均 100ms 起,还可能被杀软拦截或卡住。 若你已在写部署脚本或 CI 工具,且明确运行在管理员权限的 Windows 环境下,用 PowerShell 是可选项,但别嵌在核心业务逻辑里 绝对避免用字符串匹配
netstat -ano | findstr :8080
—— 端口号可能出现在 PID 列(比如 PID 是 8080),导致误报 Linux 下等价命令是
ss -tln | grep ':8080'
,但 C# 程序里调用它会让代码失去可移植性 绑定前检查端口可用性的正确姿势 真正要避免“Address already in use”异常,不是靠提前扫描,而是让 Socket 自己处理:用
SocketOptionName.ReuseAddress
允许端口重用(TIME_WAIT 状态下也能 bind),再配合
SO_EXCLUSIVEADDRUSE
(Windows)或
SO_REUSEPORT
(Linux/macOS)精细控制。但注意,
ReuseAddress
不等于“允许冲突绑定”,它只是放宽了内核限制。 监听前设置
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true)
是防御性标配 .NET 中
TcpListener
默认不启用
ReuseAddress
,需传入自定义
Socket
实例才能控制 多个进程绑定同一端口只有在特定条件下才合法(如 SO_REUSEPORT + 同一用户),不要假设它总能成功 所以,与其花力气反复探测,不如把异常处理写扎实:
try { var listener = new TcpListener(IPAddress.Any, 8080); listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); listener.Start(); } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.AddressAlreadyInUse) { // 真正需要关心的错误:此时再决定是换端口、报错还是等重试 }
端口占用判断没有银弹。连得通 ≠ 一定能用(可能协议不匹配),查到监听 ≠ 一定是你要防的那个服务,而
ReuseAddress
开了也不代表不会冲突——关键看你的场景到底要防御什么:是启动时避免报错?还是运行中感知依赖服务是否存活?目标不同,方案就得切开看。

相关文章