使用UPnP來穿透NAT使內網介面對外網可見

2021-07-25 20:01:24 字數 4065 閱讀 7780

在寫完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位址,內部埠號繫結到乙個外部埠號上。

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

varname 

=dns

.gethostname();

console

.writeline(

"使用者:"

+name);

//從當前host name解析ip位址,篩選ipv4位址是本機的內網ip位址。

varipv4 

=dns

.gethostentry(name)

.addresslist

.where(i 

=>i.

addressfamily 

==addressfamily

.internetwork)

.firstordefault();

console

.writeline(

"內網ip:"

+ipv4);

**如下:

console

.writeline(

"設定upnp");

//upnp繫結資訊

vareport 

=8733;

variport 

=8733;

vardescription 

="mgen測試";

//建立com型別

varupnpnat 

=new

upnpnat

();

var=upnpnat.

//錯誤判斷

if==null)

//新增之前的ipv4變數(內網ip),內部埠,和外部埠 .

add(eport, 

"tcp"

, iport, ipv4

.tostring(), 

true

, description);

console

.writeline(

"外部埠:"

, eport);

console

.writeline(

"內部埠:"

, iport);

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

設定好upnp後,開始獲取外網ip位址,可以通過這個**(此時只需要傳送乙個http get請求,然後把返回的html中的ip位址提取出來就可以了,我們用正則來提取ip位址。

**如下:

//外網ip變數

string

eip;

//正則

varregex 

=@"\b\d\.\d\.\d\.\d\b";

using

(var

webclient 

=new

webclient

())

console

.writeline(

"外網ip:"

+eip);

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

直接建立乙個tcp服務端,代表在nat下的伺服器,注意埠號要繫結到upnp設定時的內部埠。

**://在nat下的伺服器

varsocket 

=new

socket

(addressfamily

.internetwork, 

sockettype

.stream, 

protocoltype

.tcp);

//繫結內網ip和內部埠

socket

.bind(

newipendpoint

(ipv4, iport));

socket

.listen(1);

//在另乙個執行緒中執行客戶端socket

task

.run(() 

=>

);//成功連線

varclient 

=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, 

intport)

}catch

(exception

ex)

}ok。

nat內網穿透 相關研究

就是乙個socket 同時進行listen和 connect 是否可行。答案是可以的。而且此socket還需要bind同乙個本機ip port,同時,需要進行設定 setsockopt listenfd,sol socket,so reuseaddr,void value,sizeof value ...

利用frp來建立內網穿透

將包放到外網的伺服器 以下稱為服務端 和內網的伺服器 以下稱為客戶端 並解壓 服務端 配置檔案 frps.ini common bind port 7000 啟動 nohup frps c frps.ini 客戶端配置檔案 frpc.ini common server addr 填寫服務端ip se...

內網穿透NatApp使用

2 註冊並登入 註冊成功之後,登入如下圖 然後需要進行實名認證和繫結郵箱 3 購買隧道 登入成功之後,需要購買通道,可以選擇免費的通道,或者付費的,根據自己的需要 4 購買成功之後,配置隧道 5 解壓軟體,進行操作 對應隧道的authtoken authtoken 6fdb788dce71d7e 對...