一、背景
在开发客户端应用、网络工具或需要进行设备标识时,获取本机的 IP 地址是一个常见需求。但“本机 IP”其实包含两个概念:
- 内网 IP(局域网 IP): 由路由器分配的私有地址,如
192.168.x.x、10.x.x.x,用于局域网通信。 - 公网 IP(外网 IP):
本文分别介绍这两种 IP 的获取方法,并提供 C# 开箱即用的代码。
二、获取内网 IP 地址
2.1 方法一:使用 Dns.GetHostEntry(最常用)
通过获取本地主机名,再解析其所有 IP 地址,从中筛选 IPv4 地址。
using System;
using System.Net;
using System.Linq;
public static string GetLocalIPv4()
{
string hostName = Dns.GetHostName();
IPAddress[] addresses = Dns.GetHostEntry(hostName).AddressList;
IPAddress ipv4 = addresses.FirstOrDefault(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
return ipv4?.ToString() ?? "未检测到 IPv4 地址";
}
2.2 方法二:使用 NetworkInterface 获取活跃网卡 IP
该方法可以过滤掉回环地址、虚拟网卡等,更贴近实际使用的网卡。
using System.Net;
using System.Net.NetworkInformation;
using System.Linq;
public static string GetActiveLocalIPv4()
{
var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces()
.Where(n => n.OperationalStatus == OperationalStatus.Up && n.NetworkInterfaceType != NetworkInterfaceType.Loopback)
.Select(n => n.GetIPProperties())
.SelectMany(p => p.UnicastAddresses)
.Where(a => a.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
.Select(a => a.Address)
.FirstOrDefault();
return networkInterfaces?.ToString() ?? "未检测到活跃 IPv4 地址";
}
2.3 方法三:获取本机所有 IPv4 地址(包括多个网卡)
public static List<string> GetAllLocalIPv4()
{
string hostName = Dns.GetHostName();
return Dns.GetHostEntry(hostName).AddressList
.Where(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
.Select(ip => ip.ToString())
.ToList();
}
三、获取公网 IP 地址
内网 IP 无法在互联网上直接访问,因此需要借助外部服务来获取公网 IP。推荐使用 ip9.com.cn 的免费 API。
3.1 接口说明
直接调用 https://ip9.com.cn/get 即可返回当前请求的公网 IP 及归属地信息。
3.2 C# 实现
using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
public class PublicIpClient
{
private static readonly HttpClient _http = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
/// <summary>
/// 获取公网 IP 地址(仅IP字符串)
/// </summary>
public static async Task<string> GetPublicIpAsync()
{
try
{
string url = "https://ip9.com.cn/get";
var response = await _http.GetAsync(url);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
if (root.TryGetProperty("data", out var data) && data.TryGetProperty("ip", out var ipElement))
{
return ipElement.GetString();
}
return null;
}
catch
{
return null;
}
}
/// <summary>
/// 获取公网 IP 完整信息(含归属地)
/// </summary>
public static async Task<PublicIpInfo> GetPublicIpInfoAsync()
{
try
{
string url = "https://ip9.com.cn/get";
var response = await _http.GetAsync(url);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var result = JsonSerializer.Deserialize<Ip9Response>(json, options);
if (result?.Ret == 200)
{
return new PublicIpInfo
{
Ip = result.Data.Ip,
Country = result.Data.Country,
Province = result.Data.Province,
City = result.Data.City,
Area = result.Data.Area,
Isp = result.Data.Isp
};
}
return null;
}
catch
{
return null;
}
}
}
// 数据模型(与 ip9.com.cn 一致)
public class Ip9Response
{
public int Ret { get; set; }
public Ip9Data Data { get; set; }
}
public class Ip9Data
{
public string Ip { get; set; }
public string Country { get; set; }
public string Province { get; set; }
public string City { get; set; }
public string Area { get; set; }
public string Isp { get; set; }
}
public class PublicIpInfo
{
public string Ip { get; set; }
public string Country { get; set; }
public string Province { get; set; }
public string City { get; set; }
public string Area { get; set; }
public string Isp { get; set; }
}
四、综合使用示例
class Program
{
static async Task Main(string[] args)
{
// 1. 获取内网 IP
string localIp = GetLocalIPv4();
string activeLocalIp = GetActiveLocalIPv4();
var allLocalIps = GetAllLocalIPv4();
Console.WriteLine($"内网 IP(简单): {localIp}");
Console.WriteLine($"内网 IP(活跃网卡): {activeLocalIp}");
Console.WriteLine($"所有内网 IP: {string.Join(", ", allLocalIps)}");
// 2. 获取公网 IP
string publicIp = await PublicIpClient.GetPublicIpAsync();
var publicInfo = await PublicIpClient.GetPublicIpInfoAsync();
Console.WriteLine($"公网 IP: {publicIp}");
if (publicInfo != null)
{
Console.WriteLine($"归属地: {publicInfo.Country} {publicInfo.Province} {publicInfo.City} {publicInfo.Area}");
Console.WriteLine($"运营商: {publicInfo.Isp}");
}
}
}
运行示例输出
内网 IP(简单): 192.168.1.100
内网 IP(活跃网卡): 192.168.1.100
所有内网 IP: 192.168.1.100, 172.17.0.1
公网 IP: 123.45.67.89
归属地: 中国 浙江省 杭州市 西湖区
运营商: 中国电信
五、生产环境最佳实践
5.1 缓存公网 IP
公网 IP 一般不会频繁变化,可以在应用启动时获取一次并缓存,避免频繁调用外部 API。
public static class PublicIpCache
{
private static string _cachedIp;
private static DateTime _cacheTime;
private static readonly TimeSpan _cacheDuration = TimeSpan.FromHours(1);
public static async Task<string> GetPublicIpAsync()
{
if (_cachedIp != null && DateTime.Now - _cacheTime < _cacheDuration)
return _cachedIp;
var ip = await PublicIpClient.GetPublicIpAsync();
if (!string.IsNullOrEmpty(ip))
{
_cachedIp = ip;
_cacheTime = DateTime.Now;
}
return ip;
}
}
5.2 降级处理
如果公网 API 不可用,可以尝试备选方案(如 ip-api.com)或返回“未知”。
5.3 注意内网 IP 多网卡情况
使用 NetworkInterface 方式时,务必检查网卡状态和类型,避免获取到虚拟网卡、回环地址等无效 IP。
六、总结
| |
|---|
| Dns.GetHostEntry |
| NetworkInterface |
| Dns.GetHostEntry |
| 调用 ip9.com.cn API(免费、无需注册) |
以上代码均可在 .NET 6+ 项目中直接使用,无需额外引用第三方库。
该文章在 2026/9/9 10:52:30 编辑过