应从监听对象自身获取端口:TcpListener用LocalEndpoint.Port,HttpListener解析Prefix.Uri.Port,Kestrel应在启动前读取配置值;避免依赖系统级API如GetActiveTcpListeners()。

怎么查自己程序监听了哪个端口(C# 进程内)
不能直接“读取自身监听端口”,因为 .NET 没提供 GetCurrentListeningPort() 这种函数——端口是绑定时指定的,不是运行时自动注册的全局属性。你得从自己创建的监听对象里反查。
常见场景:用 TcpListener、HttpListener 或 ASP.NET Core 的 Kestrel 启动服务后,想在日志里打印“已监听 http://localhost:5001”。关键不是“查系统”,而是“别丢掉你亲手传进去的那个端口号”。
- 用
TcpListener时,端口是你构造时传入的:new TcpListener(IPAddress.Any, 8080)→ 端口就是8080 - 用
HttpListener时,Prefix里带端口,比如http://+:8080/→ 解析Uri就能拿到8080 - ASP.NET Core 中,端口来自配置(
appsettings.json或命令行),实际监听对象(KestrelServer)不暴露端口属性;推荐在启动前就记录配置值,而不是事后去挖
为什么 GetActiveTcpConnections() 查不到自己的端口?
调用 IPGlobalProperties.GetActiveTcpListeners() 确实能列出本机所有监听端口,但结果是进程无关的——它返回的是 IPEndPoint 列表,不含 PID。你无法确认某个 :5000 是你的程序还是另一个 dotnet 进程开的。
更麻烦的是权限和兼容性:GetActiveTcpListeners() 在 Linux/macOS 上不可用(.NET 6+ 跨平台 API 不支持),Windows 上还需管理员权限才能看到其他用户的监听项。
- 非管理员运行时,可能只返回
127.0.0.1:xxx,漏掉0.0.0.0:xxx - 多个服务监听同一端口(如
SO_REUSE_PORT)时,结果不可靠 - 容器或 Docker 环境中,端口映射(host:8080 → container:5000)会让系统级查询完全失真
HttpListener 和 Kestrel 的端口获取差异
HttpListener 是旧式 API,端口明文写在 Prefix 里,解析简单;Kestrel 是 ASP.NET Core 默认服务器,端口由配置驱动,运行时不可变但也不直接暴露。
-
HttpListener示例:var listener = new HttpListener(); listener.Prefixes.Add("http://+:3000/"); listener.Start(); int port = new Uri(listener.Prefixes[0]).Port; // → 3000 -
Kestrel不建议运行时查:它可能绑定多个地址(http://*:5000,https://*:5001),也可能被反向代理接管(此时真实监听端口对应用无意义)。正确做法是在Program.cs读配置:builder.Configuration.GetValue<int>("Kestrel:Endpoints:Http:Port", 5000) - 若强行反射获取
KestrelServer内部端口,会因版本升级失效(内部字段名常变),且跨平台行为不一致
最容易被忽略的复杂点:动态端口(0)和 IPv6
当你把端口设为 0(让系统自动分配),就不能再“读取”原始端口值了——必须从监听成功后的 LocalEndpoint 反查。而且这个动作必须在 Start() 之后、Accept() 之前做,否则可能还没真正绑定。
IPv6 下更易踩坑:IPAddress.Any 在双栈系统上可能同时监听 IPv4 和 IPv6,但 LocalEndpoint 返回的可能是 :::5000,而你预期的是 0.0.0.0:5000。不要硬编码字符串解析。
- 安全做法:
var listener = new TcpListener(IPAddress.Any, 0); listener.Start(); int actualPort = ((IPEndPoint)listener.LocalEndpoint).Port;
- 避免用
ToString()解析地址,改用.Port属性 - 日志中写端口时,加上协议和地址上下文,比如 “HTTP listening on http://[::]:5000”
端口不是藏在系统里的秘密,是你代码里写死、配死、或者显式申请的。盯着监听对象本身,比扫描全系统更准、更快、更稳。










