.NET(C#):使用UPnP來穿透NAT使內網介面對外網可見

來源:互聯網
上載者:User

在寫完Object 672後,軟體的一個致命問題暴露出來,如果伺服器和用戶端都在內網環境下,即雙方都通過NAT來接觸外網,那麼此時用戶端是無法直接和伺服器交流的。

解決方案可以是:

1:把伺服器部署在不存在NAT的公網環境下。

2:使用常見的NAT穿透方法比如UDP打洞,或者STUN協議,但是這些方法都需要另一個已知的部署在公網環境下的伺服器。

3:就是這篇文章主要討論的方案,即不需要部署任何公網環境下的伺服器,通過路由器支援的UPnP協議來把內網的介面綁定到公網介面上。

UPnP的一大優勢就是不會像UDP打洞那樣,內網介面不需要先向外部介面發送UDP包來把綁定的公網介面告訴NAT,而且對於對稱NAT,UDP打洞是無效的。而UPnP一旦設定成功後,內網介面完全以綁定的公網介面暴露在公網中。

 

示範程式的運行是這樣的:

 

具體過程:

1. 輸出使用者Host Name和內網IP地址。

2. 通過UPnP把內網IP地址,內部連接埠號碼綁定到一個外部連接埠號碼上。

3. 通過HTTP從外部網站擷取公網IP地址。

4. 在內網中建立TCP Socket伺服器。

5. 建立另一個TCP Socket用戶端,然後嘗試串連上面擷取的公網IP和UPnP綁定的外部連接埠。

6. 如果一切沒有問題的話,此時會成功串連到伺服器,並收到回應!

 

在.NET環境下使用Windows的UPnP組件需要現在工程中引用:NATUPnP 1.0 Type Library,這是一個COM類庫。

下面開始逐句分析原始碼,原始碼均擬使用者已加入下列命名空間:

using System.Net;                     

using System.Net.Sockets;            

using System.Text.RegularExpressions;  //提取IP時的正則

using System.Threading.Tasks;          //Task

using System.IO;                       //讀取伺服器資訊用到StreamReader

using NATUPNPLib;                      //Windows UPnP COM組件

 

首先輸出本機(也就是內網介面資訊),這個很簡單了:

//擷取Host Name

var name = Dns.GetHostName();

Console.WriteLine("使用者:" + name);

//從當前Host Name解析IP地址,篩選IPv4地址是原生內網IP地址。

var ipv4 = Dns.GetHostEntry(name).AddressList.Where(i => i.AddressFamily == AddressFamily.InterNetwork).FirstOrDefault();

Console.WriteLine("內網IP:" + ipv4);

 

接下來就是設定UPnP了,首先需要初始化UPnPNAT類型(他是一個介面,只不過通過CoClass特性把執行導向UPnPNATClass類型),接著通過UPnPNAT的StaticPortMappingCollection來添加或者刪除UPnP綁定。注意在沒有路由器或者路由器的UPnP不開啟的情況下,StaticPortMappingCollection屬性可能會返回null。

 

代碼如下:

Console.WriteLine("設定UPnP");

//UPnP綁定資訊

var eport = 8733;

var iport = 8733;

var description = "Mgen測試";

 

//建立COM類型

var upnpnat = new UPnPNAT();

var mappings = upnpnat.StaticPortMappingCollection;

 

//錯誤判斷

if (mappings == null)

{

    Console.WriteLine("沒有檢測到路由器,或者路由器不支援UPnP功能。");

    return;

}

 

//添加之前的ipv4變數(內網IP),內部連接埠,和外部連接埠

mappings.Add(eport, "TCP", iport, ipv4.ToString(), true, description);

 

Console.WriteLine("外部連接埠:{0}", eport);

Console.WriteLine("內部連接埠:{0}", iport);

 

如果成功後,你應該可以在路由器的UPnP選項中看到這些資料:

 

設定好UPnP後,開始擷取外網IP地址,可以通過這個網址(http://checkip.dyndns.org/)。此時只需要發送一個HTTP GET請求,然後把返回的HTML中的IP地址提取出來就可以了,我們用正則來提取IP地址。

代碼如下:

//外網IP變數

string eip;

//正則

var regex = @"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b";

using (var webclient = new WebClient())

{

    var rawRes = webclient.DownloadString("http://checkip.dyndns.org/");

    eip = Regex.Match(rawRes, regex).Value;

}

 

Console.WriteLine("外網IP:" + eip);

 

OK,這個時候(如果一切順利的話),一切準備工作都做好了。我們有了:內網IP,內部連接埠,外網IP,外部連接埠。那麼就可以做一個TCP串連做測試了。

 

直接建立一個TCP服務端,代表在NAT下的伺服器,注意連接埠號碼要綁定到UPnP設定時的內部連接埠。

代碼:

//在NAT下的伺服器

var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

//綁定內網IP和內部連接埠

socket.Bind(new IPEndPoint(ipv4, iport));

socket.Listen(1);

 

//在另一個線程中運行用戶端Socket

Task.Run(() =>

    {

        Task.Delay(1000).Wait();

        ClientSocket(eip, eport);

    });

 

//成功串連

var client = socket.Accept();

//伺服器向用戶端發送資訊

client.Send(Encoding.Unicode.GetBytes("=== 歡迎來到Mgen的伺服器!===" + Environment.NewLine));

 

Console.ReadKey(false);

 

上面的ClientSocket方法就是用戶端的Socket串連執行,注意TCP協議是不保留資料邊界的,因此伺服器在發送訊息時,後面加了個分行符號(Environment.NewLine),然後在用戶端接受資料時,使用Socket –> NetworkStream –> StreamReader的嵌套組合,最後由StreamReader的ReadLine讀取資料,這樣確保會讀到最後的分行符號。

 

ClientSocket方法的執行代碼:

//ip參數和port參數是公網的IP地址,和UPnP中的外部連接埠

static void ClientSocket(string ip, int port)

{

    try

    {

        Console.WriteLine("建立用戶端TCP串連");

        var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        socket.Connect(new IPEndPoint(IPAddress.Parse(ip), port));

        using (var ns = new NetworkStream(socket))

        using (var sr = new StreamReader(ns, Encoding.Unicode))

        {

            Console.WriteLine("收到來自伺服器的回應:");

            Console.WriteLine(sr.ReadLine());

        }

    }

    catch (Exception ex)

    {

        Console.WriteLine(ex.Message);

    }

}

OK。

 

原始碼下載
下載頁面
注意:連結是微軟SkyDrive頁面,下載時請用瀏覽器直接下載,用某些下載工具可能無法下載
原始碼環境:Microsoft Visual Studio Express 2012 for Windows Desktop
注意:原始碼不包含引用的外部類庫檔案

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.