基于C#实现的UPnP端口映射程序

基于C#实现的UPnP端口映射程序,包含服务器端和客户端实现,支持TCP端口穿透和自动NAT穿透:


一、核心实现原理

  1. UPnP协议:通过路由器自动映射内网端口到公网

  2. 双通道通信:服务器监听内网端口,客户端通过公网IP+映射端口连接

  3. 动态IP获取:通过外部服务获取公网IP地址


二、服务器端实现(支持自动端口映射)

1. UPnP端口映射类(UPnPHelper.cs)
csharp 复制代码
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服务不可用", 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服务端(Server.cs)
csharp 复制代码
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
        {
            // 设置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($"服务器已启动 - 外网访问地址: {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();
        }
    }
}

三、客户端实现

1. UPnP客户端类(UPnPClient.cs)
csharp 复制代码
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($"服务器响应: {response}");
        }
    }
}

四、主程序入口

1. 服务器端启动
csharp 复制代码
class Program
{
    static void Main(string[] args)
    {
        var server = new Server(8080);
        server.Start();
    }
}
2. 客户端连接
csharp 复制代码
class Program
{
    static void Main(string[] args)
    {
        var client = new UPnPClient();
        client.Connect("服务器公网IP", 8080); // 替换为实际公网IP
    }
}

五、关键功能扩展

1. 多协议支持
csharp 复制代码
// 在UPnPHelper中扩展UDP支持
public bool AddUdpPortMapping(int externalPort, int internalPort, string internalIP)
{
    return AddPortMapping(externalPort, internalPort, internalIP, ProtocolType.UDP);
}
2. 自动重连机制
csharp 复制代码
// 在客户端添加重试逻辑
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. 安全增强
csharp 复制代码
// 添加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());
        // 处理加密通信...
    }
}

参考代码 C#远程控制桌面源码 www.youwenfan.com/contentcsr/112327.html

六、部署与测试

1. 环境要求
  • Windows 10/11 专业版及以上(需启用UPnP服务)

  • 路由器开启UPnP功能

  • 防火墙允许TCP端口8080

2. 测试步骤
  1. 在服务器端运行程序

  2. 查看控制台输出的外网IP和端口

  3. 在客户端输入该地址进行连接

  4. 验证双向通信是否正常


七、常见问题解决

问题现象 解决方案
UPnP映射失败 检查路由器UPnP设置,重启路由器
无法获取外网IP 更换检测地址为https://api.ipify.org
连接超时 检查防火墙规则,确保端口开放
数据包丢失 启用TCP窗口缩放因子优化
相关推荐
xuxie992 小时前
NEXT 1 进程2
java·开发语言·jvm
三天不学习2 小时前
Linux inotify 机制详解,解决“用户实例限制”问题
linux·运维·c#
未来之窗软件服务2 小时前
AI人工智能(二十三)错误示范ASR 语音识别C#—东方仙盟练气期
人工智能·c#·语音识别·仙盟创梦ide·东方仙盟
Ljwuhe2 小时前
类与对象(中)——运算符重载
开发语言·c++
郝学胜-神的一滴2 小时前
深入理解链表:从基础到实践
开发语言·数据结构·c++·算法·链表·架构
敲敲了个代码2 小时前
vue文件自动生成路由会成为主流
开发语言·前端·javascript·vue.js·前端框架
你住过的屋檐2 小时前
【Java】虚拟线程详解
java·开发语言
霍理迪2 小时前
JS—事件高级
开发语言·javascript·ecmascript
范特西.i3 小时前
QT聊天项目(8)
开发语言·qt