获取内网IP应遍历启用的物理网卡并校验私有地址段(10.0.0.0/8、172.16.0.0/12、192.168.0.0/16),禁用Dns.GetHostName()等不可靠方式;公网IP须调用多个高可用HTTP接口(如api.ipify.org)并加超时重试,禁用HTML解析。

直接说结论:获取内网 IP 要靠遍历物理网卡,别信 Dns.GetHostName();获取公网 IP 必须走 HTTP 外部服务,且不能硬编码解析逻辑。
为什么 Dns.GetHostAddresses 常常返回 127.0.0.1 或 IPv6?
主机名解析依赖系统 hosts 文件和 DNS 配置,Dns.GetHostName() 返回的主机名可能被映射到 127.0.0.1(尤其开发环境或 Docker 容器里),Dns.GetHostAddresses 就会把回环地址也塞进来。IPv6 地址(如 ::1 或链路本地地址 fe80::...)更常见,但多数局域网通信只认 IPv4 私有地址。
真正可用的内网 IP 必须同时满足:
- 是 IPv4 地址(
AddressFamily.InterNetwork) - 不是
127.0.0.0/8、0.0.0.0、未分配地址 - 属于私有地址段:
10.0.0.0/8、172.16.0.0/12、192.168.0.0/16 - 来自启用的物理网卡(
OperationalStatus.Up且类型为Ethernet或Wireless80211)
所以推荐用 NetworkInterface.GetAllNetworkInterfaces(),而不是靠主机名反查。
怎么用 NetworkInterface 稳定提取有效内网 IP?
关键在过滤逻辑:跳过虚拟网卡(如 Hyper-V、DockerNAT)、隧道接口(Tunnel)、回环(Loopback),再校验地址是否真实私有。
示例核心逻辑:
foreach (var adapter in NetworkInterface.GetAllNetworkInterfaces())
{
if (adapter.OperationalStatus != OperationalStatus.Up ||
!new[] { NetworkInterfaceType.Ethernet, NetworkInterfaceType.Wireless80211 }.Contains(adapter.NetworkInterfaceType))
continue;
foreach (var ua in adapter.GetIPProperties().UnicastAddresses)
{
if (ua.Address.AddressFamily != AddressFamily.InterNetwork)
continue;
if (ua.Address.ToString() == "127.0.0.1" || ua.Address.ToString().StartsWith("0."))
continue;
if (IsIPv4Private(ua.Address)) // 自定义判断函数,检查是否在 10/172.16/192.168 段
return ua.Address.ToString();
}
}
注意:IsIPv4Private 必须自己实现——别用字符串匹配,要用 GetAddressBytes() 拆字节判断,否则 172.32.0.0 这类地址会被误判。
为什么不能用 ip138.com 或正则硬解析公网 IP?
这类网站 HTML 结构随时可能改,比如 "您的IP地址是:[" 可能变成 "您当前的公网IP是:" ,或者加了 JS 渲染、反爬 header,WebRequest 直接抛异常。而且 ip138.com 有访问频率限制,生产环境容易失败。
更稳妥的做法:
- 选多个备用接口,如
https://api.ipify.org(纯文本,只返回 IP)、https://icanhazip.com、https://ifconfig.me/ip - 用
HttpClient替代已过时的WebRequest或WebClient - 加超时(建议 ≤3s)和重试(最多 2 次),避免单点故障拖垮整个服务
- 别用正则从 HTML 提取——
api.ipify.org这类接口返回就是裸 IP 字符串,直接Trim()即可
简单可靠示例:
using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(3) };
string ip = await client.GetStringAsync("https://api.ipify.org").ConfigureAwait(false);
ip = ip.Trim();
if (IPAddress.TryParse(ip, out _)) return ip;
容易被忽略的边界问题
Windows 上如果“Network Connections”服务被禁用,NetworkInterface.GetAllNetworkInterfaces() 可能返回空数组或仅含 Loopback;某些企业环境会屏蔽外部 HTTP 请求,导致公网 IP 获取永远超时;还有双网卡机器(有线+WiFi 同时连),得明确业务要的是哪张卡的 IP —— 不能简单取第一个。
最保险的做法:内网 IP 兜底返回 127.0.0.1 并打日志告警,公网 IP 获取失败时缓存上次成功值(带 TTL,比如 10 分钟),而不是让调用方直接面对 null。











