使用C#取得Client端非IPv6的IP(非::1或127.0.0.1)

當在本機用VisualStudio開發ASP.NET網站時,常常會遇到用使用System.Web.HttpContext.Current.Request.UserHostAddress;時,就會遇到回傳本機IP的問題(通常是::1或127.0.0.1),這會讓一些針對IP所進行的判斷程序出現異常,因此這一篇文章就是要討論有關於這方面的解決方案。

透過C#取得本機端的真實IP

在.NET的System.Net.Dns命名空間中,存在著可以存取到本機所有IP的方法,因此我在Console中寫下下列的範例程式,供給大家參考。

using System;
using System.Linq;
using static System.Console;
namespace Simply
{
  class Program
  {
    public static void Main()
    {
      foreach (var oItem in ClientIP.getList)
      {
        WriteLine("是否為私有:" + oItem.bIsPrivate);
        WriteLine("是否為本機:" + oItem.bIsLocal);
        WriteLine("是否為IPv6:" + oItem.bIsIPv6);
        WriteLine("IP位址:" + oItem.cValue);
        WriteLine("-----");
      }
      Read();
    }
  }

  /// /// 取得Client端IP類別
  /// 
  public class ClientIP
  {
    public static System.Collections.Generic.List getList
    {
      get
      {
        System.Collections.Generic.List oData = new System.Collections.Generic.List();
        System.Net.IPHostEntry oClient = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName());
        foreach (System.Net.IPAddress oItem in oClient.AddressList)
        {
          oData.Add(new IP()
          {
            bIsPrivate = IsPrivate(oItem),
            bIsLocal = IsLocal(oItem),
            bIsIPv6 = (oItem.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6) ? true : false,
            cValue = oItem.ToString()
          });
        }
        return oData;
      }
    }

    /// /// 檢查是否為私有IP
    /// 
    private static bool IsPrivate(System.Net.IPAddress oIPAddress)
    {
      //如果不是IPv4就踢出去
      if (oIPAddress.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) { return false; }
      int[] iIP;
      try
      { //分割並利用LINQ驗證IP正確性
        iIP = oIPAddress.ToString().Split(new String[] { "." }, StringSplitOptions.RemoveEmptyEntries).Select(s => int.Parse(s)).ToArray();
      }
      catch
      { //如果有出錯一律回傳false
        return false;
      }
      //判斷是否為私有IP
      if
      (
        (iIP[0] == 10) ||
        (iIP[0] == 192 && iIP[1] == 168) ||
        (iIP[0] == 172 && (iIP[1] >= 16 && iIP[1] <= 31))
      )
      { return true; }
      else
      { return false; }
    }

    private static bool IsLocal(System.Net.IPAddress oIPAddress)
    {
      if (oIPAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
      {
        if (oIPAddress.ToString().IndexOf("127.0.0.1, ::1") != -1)
        { return true; }
        else
        { return false; }
      }
      else
      {
        if (oIPAddress.IsIPv6LinkLocal || oIPAddress.IsIPv6SiteLocal || oIPAddress.IsIPv6Multicast)
        { return true; }
        else
        { return false; }
      }
    }
  }

  /// /// IP ORM類別
  /// 
  public class IP
  {
    public bool bIsPrivate;  //是否為私有(只有在IPv4時,才有可能為True)
    public bool bIsLocal;   //是否為本機IP
    public bool bIsIPv6;    //是否為IPv6
    public string cValue;    //IP位址
  }
}

基本上我已經建立起一個類別叫做IP,所以你可以從IP中去拿到是否為私有IP?是否為IPv6?並且將其排除

除了很特別的情況,不然基本上你應該可以很順利的把本機的IPv4的真實IP值抓出來了。

程式運行畫面如下:

備註:有一些網站會介紹一些程式碼,用System.Net.Dns下面所屬的方法,利用IP或HostName來尋訪主機的網卡(例如上面的程式碼範例),藉以用Linq取出(或稱為轉換)::1、127.0.0.1或是一些延伸判斷。要注意的是這些論點都是基於你的主機網卡非常的乾淨,也就是網卡數量可能只有一片而定,若你的機器上面有很多網卡(例如虛擬網卡),那這樣的方式根本行不通的,因為你根本不知道你會抓到哪一片網卡所屬的私有IP,這樣不穩定、因環境而設事的做法,其實毫無道理可言。

C# ASP.NET IIS IP IPv4 IPv6 LocalHost ::1 127.0.0.1