基于C#實(shí)現(xiàn)的UPnP端口映射程序
基于C#實(shí)現(xiàn)的UPnP端口映射程序,包含服務(wù)器端和客戶端實(shí)現(xiàn),支持TCP端口穿透和自動NAT穿透:
一、核心實(shí)現(xiàn)原理
- UPnP協(xié)議:通過路由器自動映射內(nèi)網(wǎng)端口到公網(wǎng)
- 雙通道通信:服務(wù)器監(jiān)聽內(nèi)網(wǎng)端口,客戶端通過公網(wǎng)IP+映射端口連接
- 動態(tài)IP獲取:通過外部服務(wù)獲取公網(wǎng)IP地址
二、服務(wù)器端實(shí)現(xiàn)(支持自動端口映射)
1. UPnP端口映射類(UPnPHelper.cs)
using System;
using System.Net;
using NATUPNPLib;
public class UPnPHelper : IDisposable
{
private UPnPNAT _nat;
private IStaticPortMappingCollection _mappings;
public UPnPHelper()
{
try
{
_nat = new UPnPNAT();
_mappings = _nat.StaticPortMappingCollection;
}
catch (COMException ex)
{
throw new InvalidOperationException("UPnP服務(wù)不可用", ex);
}
}
public bool AddPortMapping(int externalPort, int internalPort, string internalIP, ProtocolType protocol, string description = "UPnP Port Forwarding")
{
try
{
_mappings.Add(externalPort, protocol.ToString().ToUpper(), internalPort, internalIP, true, description);
return true;
}
catch (COMException ex)
{
Console.WriteLine($"UPnP映射失敗: {ex.Message}");
return false;
}
}
public void RemovePortMapping(int externalPort, ProtocolType protocol)
{
try
{
_mappings.Remove(externalPort, protocol.ToString().ToUpper());
}
catch (COMException ex)
{
Console.WriteLine($"UPnP解除映射失敗: {ex.Message}");
}
}
public string GetExternalIP()
{
using (var client = new WebClient())
{
string response = client.DownloadString("http://checkip.dyndns.org/");
return System.Text.RegularExpressions.Regex.Match(response, @"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b").Value;
}
}
public void Dispose()
{
_nat?.Dispose();
}
}
public enum ProtocolType
{
TCP = 0,
UDP = 1
}
2. TCP服務(wù)端(Server.cs)
using System.Net;
using System.Net.Sockets;
public class Server
{
private Socket _serverSocket;
private UPnPHelper _upnp;
public Server(int port)
{
_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_upnp = new UPnPHelper();
}
public void Start()
{
try
{
// 設(shè)置UPnP映射
string internalIP = Dns.GetHostEntry(Dns.GetHostName()).AddressList
.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork)?.ToString();
if (!_upnp.AddPortMapping(8080, 8080, internalIP, ProtocolType.TCP))
{
throw new InvalidOperationException("UPnP端口映射失敗");
}
string externalIP = _upnp.GetExternalIP();
Console.WriteLine($"服務(wù)器已啟動 - 外網(wǎng)訪問地址: {externalIP}:8080");
_serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
_serverSocket.Listen(10);
while (true)
{
var client = _serverSocket.Accept();
_ = Task.Run(() => HandleClient(client));
}
}
finally
{
_upnp?.Dispose();
}
}
private void HandleClient(Socket client)
{
using (var ns = new NetworkStream(client))
using (var sr = new StreamReader(ns))
using (var sw = new StreamWriter(ns))
{
string msg = sr.ReadLine();
Console.WriteLine($"收到消息: {msg}");
sw.WriteLine($"ECHO: {msg}");
sw.Flush();
}
}
}
三、客戶端實(shí)現(xiàn)
1. UPnP客戶端類(UPnPClient.cs)
using System.Net;
using System.Net.Sockets;
public class UPnPClient
{
private string _externalIP;
private int _externalPort;
public void Connect(string serverIP, int serverPort)
{
// 通過UPnP獲取外部IP和端口
using (var client = new WebClient())
{
string response = client.DownloadString("http://checkip.dyndns.org/");
_externalIP = System.Text.RegularExpressions.Regex.Match(response, @"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b").Value;
}
_externalPort = serverPort;
// 建立TCP連接
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(new IPEndPoint(IPAddress.Parse(_externalIP), _externalPort));
using (var ns = new NetworkStream(socket))
using (var sr = new StreamReader(ns))
using (var sw = new StreamWriter(ns))
{
sw.WriteLine("Hello Server");
sw.Flush();
string response = sr.ReadLine();
Console.WriteLine($"服務(wù)器響應(yīng): {response}");
}
}
}
四、主程序入口
1. 服務(wù)器端啟動
class Program
{
static void Main(string[] args)
{
var server = new Server(8080);
server.Start();
}
}
2. 客戶端連接
class Program
{
static void Main(string[] args)
{
var client = new UPnPClient();
client.Connect("服務(wù)器公網(wǎng)IP", 8080); // 替換為實(shí)際公網(wǎng)IP
}
}
五、關(guān)鍵功能擴(kuò)展
1. 多協(xié)議支持
// 在UPnPHelper中擴(kuò)展UDP支持
public bool AddUdpPortMapping(int externalPort, int internalPort, string internalIP)
{
return AddPortMapping(externalPort, internalPort, internalIP, ProtocolType.UDP);
}
2. 自動重連機(jī)制
// 在客戶端添加重試邏輯
public void ConnectWithRetry(string serverIP, int serverPort, int maxRetries = 3)
{
int attempt = 0;
while (attempt < maxRetries)
{
try
{
Connect(serverIP, serverPort);
return;
}
catch (Exception ex)
{
attempt++;
Console.WriteLine($"連接失敗 ({attempt}/{maxRetries}): {ex.Message}");
Thread.Sleep(2000);
}
}
}
3. 安全增強(qiáng)
// 添加TLS加密
public void StartSecureServer(int port)
{
_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_serverSocket.Bind(new IPEndPoint(IPAddress.Any, port));
_serverSocket.Listen(10);
using (var sslStream = new SslStream(_serverSocket.Accept(), false))
{
sslStream.AuthenticateAsServer(GetCertificate());
// 處理加密通信...
}
}
六、部署與測試
1. 環(huán)境要求
- Windows 10/11 專業(yè)版及以上(需啟用UPnP服務(wù))
- 路由器開啟UPnP功能
- 防火墻允許TCP端口8080
2. 測試步驟
- 在服務(wù)器端運(yùn)行程序
- 查看控制臺輸出的外網(wǎng)IP和端口
- 在客戶端輸入該地址進(jìn)行連接
- 驗(yàn)證雙向通信是否正常
七、常見問題解決
| 問題現(xiàn)象 | 解決方案 |
|---|---|
| UPnP映射失敗 | 檢查路由器UPnP設(shè)置,重啟路由器 |
| 無法獲取外網(wǎng)IP | 更換檢測地址為https://api.ipify.org |
| 連接超時(shí) | 檢查防火墻規(guī)則,確保端口開放 |
| 數(shù)據(jù)包丟失 | 啟用TCP窗口縮放因子優(yōu)化 |
以上就是基于C#實(shí)現(xiàn)的UPnP端口映射程序的詳細(xì)內(nèi)容,更多關(guān)于C# UPnP端口映射程序的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#實(shí)現(xiàn)的自定義郵件發(fā)送類完整實(shí)例(支持多人多附件)
這篇文章主要介紹了C#實(shí)現(xiàn)的自定義郵件發(fā)送類,具有支持多人多附件的功能,涉及C#郵件操作的相關(guān)技巧,需要的朋友可以參考下2015-12-12
C#中將UTC時(shí)間轉(zhuǎn)換為JST時(shí)間的實(shí)現(xiàn)方法
在C#中,將UTC時(shí)間轉(zhuǎn)換為JST(日本標(biāo)準(zhǔn)時(shí)間,即UTC+9)時(shí)間可以通過使用 DateTime 和 TimeZoneInfo 類來實(shí)現(xiàn),JST比UTC快9小時(shí),因此可以直接進(jìn)行轉(zhuǎn)換,本文將通過代碼示例給大家介紹C#中將UTC時(shí)間轉(zhuǎn)換為JST時(shí)間,需要的朋友可以參考下2025-01-01
C#中控制反轉(zhuǎn)和依賴注入原理及實(shí)現(xiàn)
本文深入探討了IoC(控制反轉(zhuǎn))和DI(依賴注入)的概念,及在面向?qū)ο缶幊讨械膽?yīng)用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-11-11
C# DateTime與時(shí)間戳轉(zhuǎn)換實(shí)例
本篇文章主要介紹了C# DateTime與時(shí)間戳轉(zhuǎn)換實(shí)例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06
C#實(shí)現(xiàn)時(shí)間戳與標(biāo)準(zhǔn)時(shí)間的互轉(zhuǎn)
本文主要介紹了C#中時(shí)間戳與標(biāo)準(zhǔn)時(shí)間互轉(zhuǎn)的方法,其中需要注意的是基準(zhǔn)時(shí)間的問題。文中的示例代碼具有一定的學(xué)習(xí)價(jià)值,快來跟隨小編一起了解一下吧2021-12-12

