「如何在 C# 取得本機 IP」看似簡單,實則藏著許多坑:多網卡、IPv4/IPv6 混雜、回環位址、虛擬介面等。本文從早期 Dns.Resolve 過時 API 講起,逐步引導至現代、跨平台的最佳實踐。
// .NET Framework 1.x/2.0 風格,已標記 Obsolete
IPHostEntry host = Dns.Resolve(Environment.MachineName);
string ip = host.AddressList[0].ToString(); // 風險:可能拿到 IPv6 或 127.0.0.1
Dns.Resolve 已被標記為過時,且直接取 AddressList[0] 極不穩定,順序隨作業系統、網卡綁定順序而異。
使用 Dns.GetHostEntry 搭配 LINQ 過濾,精準取得想要的 IPv4 位址。
using System;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
public static class NetworkHelper
{
///
/// 取得第一張「啟用中、非回環、IPv4」的單播位址
///
public static string GetLocalIPv4()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
return host.AddressList
.Where(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) // IPv4
.FirstOrDefault()?.ToString() ?? "127.0.0.1";
}
///
/// 更穩健:透過 NetworkInterface 列舉實體網卡,排除虛擬/隧道介面
///
public static string GetPrimaryIPv4()
{
foreach (var ni in NetworkInterface.GetAllNetworkInterfaces())
{
// 忽略非啟用、回環、虛擬介面
if (ni.OperationalStatus != OperationalStatus.Up) continue;
if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue;
if (ni.NetworkInterfaceType == NetworkInterfaceType.Tunnel) continue;
var ipProps = ni.GetIPProperties();
var addr = ipProps.UnicastAddresses
.FirstOrDefault(u => u.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork
&& !IPAddress.IsLoopback(u.Address));
if (addr != null) return addr.Address.ToString();
}
return "127.0.0.1";
}
}
| 場景 | 建議做法 | 避坑指南 |
|---|---|---|
| 伺服器綁定 Listener | IPAddress.Any (0.0.0.0) | 勿硬編碼特定 IP,除非多網卡需隔離流量 |
| 向註冊中心報告自身位址 | GetPrimaryIPv4() | 需排除 Docker、VPN、Hyper-V 虛擬網卡 |
| 顯示給使用者看 | 列舉所有 IPv4 讓使用者選擇 | 單網卡環境下 GetLocalIPv4() 足夠 |
// 在 Controller 或 Middleware 中
var clientIp = HttpContext.Connection.RemoteIpAddress?.ToString();
// 若部署在反向代理(Nginx/IIS)後方,需啟用 ForwardedHeadersMiddleware
// 並讀取 X-Forwarded-For 標頭
Dns.Resolve,改用 Dns.GetHostEntry。AddressFamily.InterNetwork(IPv4)並排除 IPAddress.IsLoopback。NetworkInterface 列舉實體網卡,避開虛擬介面干擾。Dns.GetHostName() 回傳的主機名可能對應不到正確 IP,NetworkInterface 方式最穩健。