過去做專案時,偶爾會有需要記錄ASP.NET所在主機IP的需求,最常見的情境是Web Farm有多台主機,在寫入資料庫時希望一併保留該主機的IP,方便追查問題時找到正確的機器調閱IIS Log。

我常用的寫法是先用Dns.GetHostName()取得主機名稱,再用Dns.GetHostEntry().AddressList將主機名稱對應成IP網址,雖然多網卡時會傳回多筆,但原則上我們取第一筆就足以產生識別效果:
System.Net.Dns.GetHostEntry(Dns.GetHostName()).AddressList[0].ToString();

最近發現上述的寫法在Windows 2008/Windows 7上會有問題!

Vista以後的Windows作業系統,IPv6已成預設選項,網卡預設會啟用IPv6協定,所以Dns.GetHostEntry().AddressList傳回的第一筆將會是類似fe80::876:605b:8f95:dd2a%13格式的Link-local IPv6網址。即便停用IPv6協定,第一筆傳回的仍不是IPv4網址,而是::1的本機IPv6網址(相當於127.0.0.1)。

為了克服這個問題,我在程式加上First(o => o.AddressFamily == AddressFamily.InterNetwork),變成查IPv4的第一筆IP位址就可避開IPv6問題:
Dns.GetHostEntry(Dns.GetHostName()).AddressList.First(o => o.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).ToString();

這裡用了LINQ的First來簡化程式碼,若是ASP.NET 3.5以前的版本,則可用迴圈替代:

        string ipStr = "";
        foreach (IPAddress ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
            if (ip.AddressFamily == AddressFamily.InterNetwork)
            {
                ipStr = ip.ToString();
                break;
            }

以下是測試網頁:

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Net" %>
<%@ Import Namespace="System.Linq" %>
<script runat="server">
    void Page_Load(object sender, EventArgs e)
    {
        foreach (IPAddress ip in 
                    Dns.GetHostEntry(Dns.GetHostName()).AddressList)
            Response.Write("<li>" + ip.ToString());
        string ip1 = 
            Dns.GetHostEntry(Dns.GetHostName()).AddressList[0].ToString();
        string ip2 = 
            Dns.GetHostEntry(Dns.GetHostName()).AddressList
            .First(o => o.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
            .ToString();
        Response.Write(string.Format("<hr />IP1:{0}<br />IP2:{1}", ip1, ip2));
        Response.End();
    }
</script>

2011/01/01補充: 有網友詢問為何不用Request.ServerVariables["LOCAL_ADDR"]? 原因是該變數是以Request觀點取得的網路IP位址,而Web Farm主機的每一台主機對外呈現的都是同一個IP,再靠Layer 4 Switch或負載平衡機制分配到不同主機上,故Web Farm每一台主機用Request.ServerVariables["LOCAL_ADDR"]將會是相同的,用文中的方法才有辦法取得真實的IP位址。


Comments

# by 91

喔喔!我要把這篇文章收起來! 感謝黑大的分享~

# by shihwei

感謝黑大,之前在抓ServerIP也有部分電腦出現「::1」的狀況, 後來是使用Dns.Resolve(Dns.GetHostName()).AddressList(0).ToString()找出ServerIP, 只是一直沒有什頭緒為什麼會出「::1」,後來看了「C:\Windows\System32\drivers\etc\hosts」中有行「# ::1 localhost」時,有種說不出的感覺…

# by 汽車借款

很有用處耶,感謝版主的分享!

# by shihwei

哦… 我剛才查到Dns.Resolve 這個 API 現在已經過時… XD 還是用黑大的方法比較明確!!!

Post a comment