1. 程式人生 > >C#網路程式設計筆記:獲取主機的內網/公網IP以及主機的隨機可用埠

C#網路程式設計筆記:獲取主機的內網/公網IP以及主機的隨機可用埠

網路程式設計中,在測試App時,一般是先在本機、LAN進行測試,然後再在Internet環境測試。

大家比較熟悉的,本機環回(loopback)地址(IPv4)恆定為127.0.0.1,主機名localhost二者等價。

環回地址測試比較簡單,因為這個地址是一成不變的。但是LAN測試就需要花費一些心思。

本文只關注如何獲取主機IP(以及埠,Port),其他內容會在本系列後續進行更新。

很簡單,我們可以通過多種方式檢視本機IP,比較常用的方法是在命令列視窗輸入以下命令

ipconfig /all

關於ipconfig的具體使用方法,可以鍵入 

ipconfig /?

下面截圖是我在本機上的測試(部分敏感資訊已經矇蔽)


因為我之前安裝了VMware,並啟用了VMNet虛擬網絡卡,所以這個命令可以看到不止一個IP

另外注意,在IP列表中還包含IPv6地址資訊

好了,接下來就是如何使用程式碼獲取IP列表,其中一種方法如下所述

// using System.Net;
// Get Localhost IP (LAN)
public static string[] GetLocalHostIP()
{
    string hostName = Dns.GetHostName();
    IPAddress[] ipAddrs = Dns.GetHostAddresses(hostName);
    int count=ipAddrs.Length;
    string[] ss=new string[count];
    for(int i=0;i<count;++i)
    {
        ss[i]=ipAddrs[i].ToString();
    }
 
    return ss;
}

IPAddress類包含一些方法,其中就有關於判斷IP地址是否IPv6格式的,此處不做深入分析。

然後是如何獲取可用埠。

關於“埠”,這裡再囉嗦一下吧。

我們知道,網路通訊中,主機可能只有一個IP地址,主機之間(實際上是程式或程序/執行緒之間)進行通訊,可能會有多個例項,比如我們可能會同時開啟多個網頁,網路客戶端等等,這樣僅僅一個IP無法唯一地標識這些例項,因此就引入了“埠”這一概念。

舉個例子,假設主機IP為192.168.1.101,現在有兩個網路App假設為A,B,他們可以用兩個不同的編號來區分,假設它們的編號分別為1300和1301,那麼在網路上就可以使用192.168.1.101:1300標記A,用192.168.1.101:1301標記B,這個例子比較粗糙而且也不太準確,但基本上可以讓入門者對“埠”這個概念不再如此模糊。

以上這些廢話僅供參考,詳細內容請參考專業文件。

進入正題,下面是一段程式碼,獲取隨機(可用的)埠

// using System.Net.NetworkInformation;
 
// Get a random available port
public static int GetRandAvailablePort()
{
    const int MIN_PORT_N = 1250;
    const int MAX_PORT_N = 8000;
    int MID = (MIN_PORT_N + 9 * MAX_PORT_N) / 10;
    Random rand = new Random();
    int start_port = rand.Next(MIN_PORT_N, MID);
    for (int i = start_port; i <= MAX_PORT_N; i++)
    {
        if (PortIsAvailable(i)) return i;
    }
 
    return -1;
}
 
// Get the used port list
public static List<int> PortIsUsed()
{
    IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
    IPEndPoint[] ipsTCP = ipGlobalProperties.GetActiveTcpListeners();
    IPEndPoint[] ipsUDP = ipGlobalProperties.GetActiveUdpListeners();
    TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();
 
    List<int> allPorts = new List<int>();
    foreach (IPEndPoint ep in ipsTCP) allPorts.Add(ep.Port);
    foreach (IPEndPoint ep in ipsUDP) allPorts.Add(ep.Port);
    foreach (TcpConnectionInformation conn in tcpConnInfoArray) allPorts.Add(conn.LocalEndPoint.Port);
 
    return allPorts;
}
 
// Check whether the port is in the used list
public static bool PortIsAvailable(int port)
{
    bool isAvailable = true;
    List<int> portUsed = PortIsUsed();
 
    foreach (int p in portUsed)
    {
        if (p == port)
        {
            isAvailable = false; 
            break;
        }
    }
 
    return isAvailable;
}

至此,就可以編寫應用程式並在本機或者內網進行測試了。

但是後期(一般是大規模或更加正式的的)測試就需要在Internet上進行了,那麼下面就關注如何獲取本機的外網IP了, 所謂外網IP就是在因特網上相互通訊時,對方能夠由此找到本主機的地址。只有當你的主機獨佔一個子網(比如只有一個主機的LAN)的時候,不用區分內網和外網(因為進、出口都是一個)。而實際情況一般都是這樣的,每個LAN都包含很多主機,常見的是校園網,公司網,小區網等等,這個LAN可能只有 數個甚至一個路由器與外界互聯,這樣做有很多好處,讀者可以自行分析,此處不再廢話。

本文以及網路上很多文章裡都是採用一種間接的查詢方式來獲取本機的外網IP,大致步驟如下:

首先向ip138這樣的主機地址查詢網站傳送請求(只要訪問或登入這些網站,網站就能反饋本機的公網IP),然後讀取網站(伺服器)返回的頁面(文件),分析文件的文字內容,查詢相應欄位截取出來就是本機的公網IP了,參見下圖


本文將要給出的方法中使用另外一個查詢網站,具體程式碼如下

// using System.Net;
// using System.IO;
 
// Get Host IP (Internet)
public static string GetServerNetIP()
{
    Uri uri = new Uri("http://www.ikaka.com/ip/index.asp");
    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
    req.Method = "POST";
    req.ContentType = "application/x-www-form-urlencoded";
    req.ContentLength = 0;
    req.CookieContainer = new CookieContainer();
    req.GetRequestStream().Write(new byte[0], 0, 0);
    HttpWebResponse res = (HttpWebResponse)(req.GetResponse());
    StreamReader sr = new StreamReader(res.GetResponseStream(), Encoding.GetEncoding("GB2312"));
    string srcText = sr.ReadToEnd();
    sr.Close();
    req.Abort();
    res.Close();
    Match m = Regex.Match(srcText, @"[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}");
    return m.ToString();
}
一般情況下這些程式碼是沒問題的。本方法的核心在於讀取網頁原始碼然後使用正則表示式搜尋IP地址字串,按照這個思路,就可將程式碼中的Uri按照實際情況進行修改。這個示例讀取到網頁原始碼如下(截取了一小部分)
本文提到的3個主要任務:獲取內網/外網IP以及隨即可用埠,至此都已經順利完成。其他內容會在系列的後續持續更新。本文中的程式碼可能會與網路上的其他程式碼有相似甚至是相同之處,但本文的程式碼全部公開,並且可以自由轉載(不包含任何原創宣告),但建議引用本文連結或者本段文字。本文原創,博文地址