LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

C#实现混合P2P点对点直接连接方案(TCP + UDP),如果TCP连接失败则改用UDP连接

admin
2025年6月23日 17:25 本文热度 69

下面是一个整合了TCP和UDP打洞技术的完整P2P解决方案。该方案优先尝试TCP连接,如果失败则自动回退到UDP连接,最大程度提高P2P连接成功率。

整合后的P2P协调服务器方案

下面提供完整的TCP/UDP混合P2P协调服务器方案代码,该服务器同时处理TCP和UDP请求,支持双协议P2P连接:

1. 协调服务器 (HybridCoordinatorServer.cs)

using System;

using System.Collections.Generic;

using System.Net;

using System.Net.Sockets;

using System.Text;

using System.Threading;

using System.IO;


class HybridCoordinatorServer

{

    private const int ServerPort = 11000;

    private TcpListener _tcpListener;

    private UdpClient _udpServer;

    

    private readonly Dictionary<string, IPEndPoint> _tcpEndpoints = new();

    private readonly Dictionary<string, IPEndPoint> _udpEndpoints = new();

    private readonly Dictionary<string, TcpClient> _tcpConnections = new();

    

    private readonly object _lock = new object();

    private bool _isRunning = true;


    public HybridCoordinatorServer()

    {

        try

        {

            _tcpListener = new TcpListener(IPAddress.Any, ServerPort);

            _udpServer = new UdpClient(ServerPort);

            Console.WriteLine($"混合协调服务器启动,监听端口: {ServerPort}");

        }

        catch (Exception ex)

        {

            Console.WriteLine($"服务器初始化失败: {ex.Message}");

            Environment.Exit(1);

        }

    }


    public void Start()

    {

        // 启动TCP监听线程

        new Thread(ListenTcp).Start();

        

        // 启动UDP监听线程

        new Thread(ListenUdp).Start();

        

        // 启动心跳检测线程

        new Thread(HeartbeatMonitor).Start();

        

        Console.WriteLine("服务器运行中. 按任意键停止...");

        Console.ReadKey();

        Stop();

    }


    private void ListenTcp()

    {

        try

        {

            _tcpListener.Start();

            Console.WriteLine("TCP监听已启动");

            

            while (_isRunning)

            {

                TcpClient client = _tcpListener.AcceptTcpClient();

                new Thread(() => HandleTcpClient(client)).Start();

            }

        }

        catch (Exception ex)

        {

            if (_isRunning) Console.WriteLine($"TCP监听错误: {ex.Message}");

        }

    }


    private void HandleTcpClient(TcpClient client)

    {

        string clientId = null;

        try

        {

            NetworkStream stream = client.GetStream();

            byte[] buffer = new byte[1024];

            int bytesRead = stream.Read(buffer, 0, buffer.Length);

            if (bytesRead == 0) return;


            string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);

            string[] parts = message.Split(':');

            if (parts.Length < 2) return;


            string command = parts[0];

            clientId = parts[1];

            var clientEP = (IPEndPoint)client.Client.RemoteEndPoint;


            Console.WriteLine($"[TCP] {clientId} 连接: {clientEP}");


            lock (_lock)

            {

                if (command == "REGISTER")

                {

                    _tcpEndpoints[clientId] = clientEP;

                    _tcpConnections[clientId] = client;

                    Console.WriteLine($"[注册] TCP: {clientId} -> {clientEP}");

                    

                    // 响应客户端

                    SendTcpResponse(client, "REGISTERED");

                    

                    // 请求客户端UDP端点

                    SendTcpResponse(client, "REQUEST_UDP");

                }

                else if (command == "CONNECT" && parts.Length > 2)

                {

                    string targetId = parts[2];

                    Console.WriteLine($"[连接请求] {clientId} -> {targetId}");

                    HandleConnectionRequest(clientId, targetId);

                }

            }


            // 处理后续消息

            while (_isRunning)

            {

                bytesRead = stream.Read(buffer, 0, buffer.Length);

                if (bytesRead == 0) break;


                message = Encoding.ASCII.GetString(buffer, 0, bytesRead);

                parts = message.Split(':');

                command = parts[0];


                if (command == "CONNECT" && parts.Length > 2)

                {

                    string targetId = parts[2];

                    Console.WriteLine($"[连接请求] {clientId} -> {targetId}");

                    HandleConnectionRequest(clientId, targetId);

                }

                else if (command == "UDP_PORT")

                {

                    if (parts.Length > 2 && int.TryParse(parts[2], out int udpPort))

                    {

                        lock (_lock)

                        {

                            var udpEp = new IPEndPoint(clientEP.Address, udpPort);

                            _udpEndpoints[clientId] = udpEp;

                            Console.WriteLine($"[注册] UDP: {clientId} -> {udpEp}");

                        }

                    }

                }

            }

        }

        catch (Exception ex)

        {

            Console.WriteLine($"处理TCP客户端错误: {ex.Message}");

        }

        finally

        {

            if (clientId != null)

            {

                lock (_lock)

                {

                    _tcpConnections.Remove(clientId);

                    _tcpEndpoints.Remove(clientId);

                    _udpEndpoints.Remove(clientId);

                }

            }

            client.Close();

        }

    }


    private void ListenUdp()

    {

        try

        {

            Console.WriteLine("UDP监听已启动");

            

            while (_isRunning)

            {

                IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);

                byte[] data = _udpServer.Receive(ref remoteEP);

                string message = Encoding.ASCII.GetString(data);

                

                string[] parts = message.Split(':');

                if (parts.Length < 2) continue;


                string command = parts[0];

                string clientId = parts[1];


                lock (_lock)

                {

                    if (command == "REGISTER_UDP")

                    {

                        _udpEndpoints[clientId] = remoteEP;

                        Console.WriteLine($"[注册] UDP: {clientId} -> {remoteEP}");

                    }

                    else if (command == "HEARTBEAT")

                    {

                        // 更新UDP端点(NAT可能改变端口)

                        _udpEndpoints[clientId] = remoteEP;

                        SendUdpResponse(remoteEP, "HEARTBEAT_ACK");

                    }

                }

            }

        }

        catch (Exception ex)

        {

            if (_isRunning) Console.WriteLine($"UDP监听错误: {ex.Message}");

        }

    }


    private void HeartbeatMonitor()

    {

        while (_isRunning)

        {

            Thread.Sleep(30000); // 每30秒检查一次

            

            lock (_lock)

            {

                Console.WriteLine($"[状态] 客户端数: {_tcpConnections.Count} TCP, {_udpEndpoints.Count} UDP");

                

                // 检测不活跃的TCP客户端

                List<string> tcpToRemove = new List<string>();

                foreach (var kvp in _tcpConnections)

                {

                    if (!IsTcpClientConnected(kvp.Value))

                    {

                        tcpToRemove.Add(kvp.Key);

                    }

                }

                

                foreach (string clientId in tcpToRemove)

                {

                    _tcpConnections.Remove(clientId);

                    _tcpEndpoints.Remove(clientId);

                    _udpEndpoints.Remove(clientId);

                    Console.WriteLine($"[清理] 移除不活跃TCP客户端: {clientId}");

                }

            }

        }

    }


    private bool IsTcpClientConnected(TcpClient client)

    {

        try

        {

            if (client.Client.Poll(0, SelectMode.SelectRead))

            {

                byte[] buff = new byte[1];

                return client.Client.Receive(buff, SocketFlags.Peek) != 0;

            }

            return true;

        }

        catch

        {

            return false;

        }

    }


    private void HandleConnectionRequest(string fromClient, string toClient)

    {

        lock (_lock)

        {

            if (!_tcpConnections.ContainsKey(fromClient))

            {

                Console.WriteLine($"[错误] 源客户端未连接: {fromClient}");

                return;

            }

            

            if (!_tcpConnections.ContainsKey(toClient))

            {

                SendTcpResponse(_tcpConnections[fromClient], "ERROR:目标客户端未连接");

                Console.WriteLine($"[错误] 目标客户端未连接: {toClient}");

                return;

            }

            

            if (!_tcpEndpoints.TryGetValue(fromClient, out IPEndPoint fromTcpEp) ||

                !_tcpEndpoints.TryGetValue(toClient, out IPEndPoint toTcpEp) ||

                !_udpEndpoints.TryGetValue(fromClient, out IPEndPoint fromUdpEp) ||

                !_udpEndpoints.TryGetValue(toClient, out IPEndPoint toUdpEp))

            {

                SendTcpResponse(_tcpConnections[fromClient], "ERROR:端点信息不完整");

                Console.WriteLine($"[错误] 端点信息不完整: {fromClient} -> {toClient}");

                return;

            }


            // 交换端点信息

            string fromMessage = $"PEER_INFO:{toClient}:{toTcpEp}:{toUdpEp}";

            string toMessage = $"PEER_INFO:{fromClient}:{fromTcpEp}:{fromUdpEp}";

            

            SendTcpResponse(_tcpConnections[fromClient], fromMessage);

            SendTcpResponse(_tcpConnections[toClient], toMessage);

            

            Console.WriteLine($"[端点交换] {fromClient} <-> {toClient}");

            Console.WriteLine($"  TCP: {fromTcpEp} <-> {toTcpEp}");

            Console.WriteLine($"  UDP: {fromUdpEp} <-> {toUdpEp}");

        }

    }


    private void SendTcpResponse(TcpClient client, string message)

    {

        try

        {

            if (client.Connected)

            {

                NetworkStream stream = client.GetStream();

                byte[] data = Encoding.ASCII.GetBytes(message);

                stream.Write(data, 0, data.Length);

            }

        }

        catch (Exception ex)

        {

            Console.WriteLine($"发送TCP响应错误: {ex.Message}");

        }

    }


    private void SendUdpResponse(IPEndPoint ep, string message)

    {

        try

        {

            byte[] data = Encoding.ASCII.GetBytes(message);

            _udpServer.Send(data, data.Length, ep);

        }

        catch (Exception ex)

        {

            Console.WriteLine($"发送UDP响应错误: {ex.Message}");

        }

    }


    private void Stop()

    {

        Console.WriteLine("停止服务器...");

        _isRunning = false;

        

        try

        {

            lock (_lock)

            {

                foreach (var client in _tcpConnections.Values)

                {

                    try { client.Close(); } catch { }

                }

                _tcpConnections.Clear();

                _tcpEndpoints.Clear();

                _udpEndpoints.Clear();

            }

            

            _tcpListener.Stop();

            _udpServer.Close();

            Console.WriteLine("服务器已停止");

        }

        catch (Exception ex)

        {

            Console.WriteLine($"停止服务器错误: {ex.Message}");

        }

    }


    static void Main(string[] args)

    {

        Console.Title = "P2P混合协调服务器";

        Console.WriteLine("=== P2P混合协调服务器 ===");

        Console.WriteLine("支持TCP和UDP协议穿透");

        Console.WriteLine("端口: " + ServerPort);

        Console.WriteLine(new string('=', 50));

        

        var server = new HybridCoordinatorServer();

        server.Start();

    }

}

2. 混合客户端 (HybridP2PClient.cs)

using System;

using System.Net;

using System.Net.Sockets;

using System.Text;

using System.Threading;


class HybridP2PClient

{

    // 配置参数

    private readonly string _serverIp;

    private readonly int _serverPort;

    private readonly string _clientId;

    private const int PunchAttempts = 5;

    private const int PunchInterval = 1000; // ms


    // 网络组件

    private TcpClient _tcpServerConnection;

    private UdpClient _udpClient;

    private TcpListener _tcpListener;

    private NetworkStream _tcpStream;

    

    // 状态变量

    private IPEndPoint _peerUdpEP;

    private IPEndPoint _peerTcpEP;

    private bool _isConnected = false;

    private bool _useTCP = true;

    private bool _isRunning = true;

    private int _localUdpPort;


    public HybridP2PClient(string serverIp, int serverPort, string clientId)

    {

        _serverIp = serverIp;

        _serverPort = serverPort;

        _clientId = clientId;

    }


    public void Start()

    {

        // 连接到协调服务器

        ConnectToServer();


        // 启动本地TCP监听器

        StartTcpListener();

        

        // 启动本地UDP监听器

        StartUdpListener();


        // 启动UDP心跳线程

        new Thread(UdpHeartbeat).Start();


        Console.WriteLine("输入要连接的客户端ID (或按回车退出):");

        while (_isRunning)

        {

            string targetId = Console.ReadLine();

            if (string.IsNullOrEmpty(targetId)) break;


            RequestConnection(targetId);

        }

        

        // 清理资源

        _isRunning = false;

        _tcpListener?.Stop();

        _udpClient?.Close();

        _tcpServerConnection?.Close();

    }


    #region 服务器通信

    private void ConnectToServer()

    {

        try

        {

            // 使用TCP连接服务器

            _tcpServerConnection = new TcpClient(_serverIp, _serverPort);

            _tcpStream = _tcpServerConnection.GetStream();

            Console.WriteLine("已连接到协调服务器");


            // 注册到服务器

            string registerMsg = $"REGISTER:{_clientId}";

            byte[] data = Encoding.ASCII.GetBytes(registerMsg);

            _tcpStream.Write(data, 0, data.Length);


            // 启动接收服务器消息的线程

            new Thread(ReceiveFromServer).Start();

        }

        catch (Exception ex)

        {

            Console.WriteLine($"连接服务器失败: {ex.Message}");

        }

    }


    private void RequestConnection(string targetId)

    {

        if (_isConnected)

        {

            Console.WriteLine("已连接到对等方,请先断开当前连接");

            return;

        }

        

        string message = $"CONNECT:{_clientId}:{targetId}";

        byte[] data = Encoding.ASCII.GetBytes(message);

        _tcpStream.Write(data, 0, data.Length);

    }


    private void ReceiveFromServer()

    {

        try

        {

            byte[] buffer = new byte[1024];

            

            while (_isRunning)

            {

                int bytesRead = _tcpStream.Read(buffer, 0, buffer.Length);

                if (bytesRead == 0) break;


                string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);

                Console.WriteLine($"收到服务器消息: {message}");


                string[] parts = message.Split(':');

                if (parts[0] == "PEER_INFO")

                {

                    HandlePeerInfo(parts);

                }

                else if (parts[0] == "REQUEST_UDP")

                {

                    // 服务器请求UDP端口信息

                    SendUdpPortInfo();

                }

                else if (parts[0] == "ERROR")

                {

                    Console.WriteLine($"服务器错误: {message.Substring(6)}");

                }

            }

        }

        catch (Exception ex)

        {

            Console.WriteLine($"接收服务器消息错误: {ex.Message}");

        }

    }


    private void SendUdpPortInfo()

    {

        try

        {

            string message = $"UDP_PORT:{_clientId}:{_localUdpPort}";

            byte[] data = Encoding.ASCII.GetBytes(message);

            _tcpStream.Write(data, 0, data.Length);

            Console.WriteLine($"已发送UDP端口信息: {_localUdpPort}");

        }

        catch (Exception ex)

        {

            Console.WriteLine($"发送UDP端口信息失败: {ex.Message}");

        }

    }


    private void HandlePeerInfo(string[] parts)

    {

        // 格式: PEER_INFO:<peer_id>:<tcp_ep>:<udp_ep>

        if (parts.Length < 4) return;

        

        string peerId = parts[1];

        

        // 解析TCP端点

        string[] tcpParts = parts[2].Split(':');

        if (tcpParts.Length < 2) return;

        _peerTcpEP = new IPEndPoint(

            IPAddress.Parse(tcpParts[0]), 

            int.Parse(tcpParts[1]));

        

        // 解析UDP端点

        string[] udpParts = parts[3].Split(':');

        if (udpParts.Length < 2) return;

        _peerUdpEP = new IPEndPoint(

            IPAddress.Parse(udpParts[0]), 

            int.Parse(udpParts[1]));

        

        Console.WriteLine($"目标客户端信息: TCP={_peerTcpEP}, UDP={_peerUdpEP}");

        

        // 启动打洞线程

        new Thread(AttemptPunch).Start();

    }

    #endregion


    #region 打洞与连接

    private void AttemptPunch()

    {

        Console.WriteLine("开始P2P连接尝试...");

        

        // 优先尝试TCP连接

        if (AttemptTcpConnection())

        {

            _useTCP = true;

            _isConnected = true;

            Console.WriteLine("TCP连接成功!使用TCP进行通信");

            StartChatting();

            return;

        }

        

        Console.WriteLine("TCP连接失败,尝试UDP打洞...");

        

        // TCP失败后尝试UDP打洞

        if (AttemptUdpPunch())

        {

            _useTCP = false;

            _isConnected = true;

            Console.WriteLine("UDP打洞成功!使用UDP进行通信");

            StartChatting();

            return;

        }

        

        Console.WriteLine("所有连接尝试失败,无法建立P2P连接");

    }


    private bool AttemptTcpConnection()

    {

        Console.WriteLine("尝试TCP打洞连接...");

        

        for (int i = 0; i < PunchAttempts; i++)

        {

            try

            {

                Console.WriteLine($"TCP尝试 {i+1}/{PunchAttempts} 连接到 {_peerTcpEP}");

                var tcpClient = new TcpClient();

                tcpClient.Connect(_peerTcpEP);

                

                // 保存连接

                _tcpPeerConnection = tcpClient;

                return true;

            }

            catch (SocketException sex)

            {

                Console.WriteLine($"TCP连接失败: {sex.SocketErrorCode}");

            }

            catch (Exception ex)

            {

                Console.WriteLine($"TCP连接异常: {ex.Message}");

            }

            

            Thread.Sleep(PunchInterval);

        }

        

        return false;

    }


    private bool _udpConnected = false;

    

    private bool AttemptUdpPunch()

    {

        Console.WriteLine("尝试UDP打洞...");

        _udpConnected = false;

        

        // 发送多个打洞包(确保穿过NAT)

        for (int i = 0; i < PunchAttempts; i++)

        {

            try

            {

                string message = $"PUNCH:{_clientId}:{i}";

                byte[] data = Encoding.ASCII.GetBytes(message);

                _udpClient.Send(data, data.Length, _peerUdpEP);

                Console.WriteLine($"发送UDP打洞包到 {_peerUdpEP}");

            }

            catch (Exception ex)

            {

                Console.WriteLine($"发送UDP打洞包失败: {ex.Message}");

            }

            

            Thread.Sleep(PunchInterval);

        }

        

        // 检查是否收到对方消息

        Console.WriteLine("等待UDP连接确认... (10秒)");

        DateTime startTime = DateTime.Now;

        

        while ((DateTime.Now - startTime).TotalSeconds < 10)

        {

            if (_udpConnected)

            {

                return true;

            }

            Thread.Sleep(100);

        }

        

        return false;

    }

    #endregion


    #region 网络监听

    private void StartTcpListener()

    {

        try

        {

            // 绑定随机本地端口

            _tcpListener = new TcpListener(IPAddress.Any, 0);

            _tcpListener.Start();

            var localEp = (IPEndPoint)_tcpListener.LocalEndpoint;

            Console.WriteLine($"TCP监听端口: {localEp.Port}");

            

            // 启动接受TCP连接的线程

            new Thread(() =>

            {

                while (_isRunning)

                {

                    try

                    {

                        TcpClient peer = _tcpListener.AcceptTcpClient();

                        var remoteEp = (IPEndPoint)peer.Client.RemoteEndPoint;

                        

                        if (_isConnected)

                        {

                            Console.WriteLine($"已连接,拒绝来自 {remoteEp} 的TCP连接");

                            peer.Close();

                            continue;

                        }

                        

                        _tcpPeerConnection = peer;

                        _isConnected = true;

                        _useTCP = true;

                        Console.WriteLine($"接受来自 {remoteEp} 的TCP连接");

                        StartChatting();

                    }

                    catch (Exception ex)

                    {

                        if (_isRunning) Console.WriteLine($"接受TCP连接错误: {ex.Message}");

                    }

                }

            }).Start();

        }

        catch (Exception ex)

        {

            Console.WriteLine($"启动TCP监听器失败: {ex.Message}");

        }

    }


    private void StartUdpListener()

    {

        try

        {

            // 绑定随机本地端口

            _udpClient = new UdpClient(0);

            var localEp = (IPEndPoint)_udpClient.Client.LocalEndPoint;

            _localUdpPort = localEp.Port;

            Console.WriteLine($"UDP监听端口: {localEp.Port}");

            

            // 启动UDP接收线程

            new Thread(() =>

            {

                while (_isRunning)

                {

                    try

                    {

                        IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);

                        byte[] data = _udpClient.Receive(ref remoteEP);

                        string message = Encoding.ASCII.GetString(data);

                        

                        // 检查是否是来自目标对等方的消息

                        if (_peerUdpEP != null && 

                            (remoteEP.Address.Equals(_peerUdpEP.Address) || 

                             remoteEP.Port == _peerUdpEP.Port))

                        {

                            if (message.StartsWith("PUNCH:"))

                            {

                                Console.WriteLine($"收到UDP打洞包: {message}");

                                if (!_isConnected) _udpConnected = true;

                            }

                            else if (message.StartsWith("MSG:"))

                            {

                                Console.WriteLine($"收到UDP消息: {message.Substring(4)}");

                            }

                        }

                    }

                    catch (Exception ex)

                    {

                        if (_isRunning) Console.WriteLine($"接收UDP消息错误: {ex.Message}");

                    }

                }

            }).Start();

        }

        catch (Exception ex)

        {

            Console.WriteLine($"启动UDP监听器失败: {ex.Message}");

        }

    }


    private void UdpHeartbeat()

    {

        var serverEP = new IPEndPoint(IPAddress.Parse(_serverIp), _serverPort);

        

        while (_isRunning)

        {

            try

            {

                // 每30秒发送一次心跳

                Thread.Sleep(30000);

                

                string message = $"HEARTBEAT:{_clientId}";

                byte[] data = Encoding.ASCII.GetBytes(message);

                _udpClient.Send(data, data.Length, serverEP);

            }

            catch (Exception ex)

            {

                Console.WriteLine($"发送UDP心跳失败: {ex.Message}");

            }

        }

    }

    #endregion


    #region 通信处理

    private TcpClient _tcpPeerConnection;

    

    private void StartChatting()

    {

        if (_useTCP)

        {

            // TCP通信模式

            new Thread(() => ReceiveTcpMessages(_tcpPeerConnection)).Start();

            new Thread(() => SendTcpMessages(_tcpPeerConnection)).Start();

        }

        else

        {

            // UDP通信模式

            new Thread(SendUdpMessages).Start();

        }

    }


    private void ReceiveTcpMessages(TcpClient peer)

    {

        try

        {

            NetworkStream stream = peer.GetStream();

            byte[] buffer = new byte[1024];

            

            while (_isConnected)

            {

                int bytesRead = stream.Read(buffer, 0, buffer.Length);

                if (bytesRead == 0) break;

                

                string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);

                Console.WriteLine($"对方(TCP): {message}");

            }

        }

        catch (Exception ex)

        {

            Console.WriteLine($"接收TCP消息错误: {ex.Message}");

        }

        finally

        {

            _isConnected = false;

            peer.Close();

            Console.WriteLine("TCP连接已关闭");

        }

    }


    private void SendTcpMessages(TcpClient peer)

    {

        try

        {

            NetworkStream stream = peer.GetStream();

            Console.WriteLine("输入消息开始聊天 (输入'exit'退出):");

            

            while (_isConnected)

            {

                string message = Console.ReadLine();

                if (message == "exit") break;

                

                byte[] data = Encoding.ASCII.GetBytes(message);

                stream.Write(data, 0, data.Length);

            }

        }

        catch (Exception ex)

        {

            Console.WriteLine($"发送TCP消息错误: {ex.Message}");

        }

        finally

        {

            _isConnected = false;

            peer.Close();

        }

    }


    private void SendUdpMessages()

    {

        Console.WriteLine("输入消息开始聊天 (输入'exit'退出):");

        

        while (_isConnected)

        {

            try

            {

                string message = Console.ReadLine();

                if (message == "exit") break;

                

                byte[] data = Encoding.ASCII.GetBytes($"MSG:{message}");

                _udpClient.Send(data, data.Length, _peerUdpEP);

            }

            catch (Exception ex)

            {

                Console.WriteLine($"发送UDP消息错误: {ex.Message}");

            }

        }

        

        _isConnected = false;

    }

    #endregion


    static void Main(string[] args)

    {

        Console.Write("输入协调服务器IP: ");

        string serverIp = Console.ReadLine();

        

        Console.Write("输入客户端ID: ");

        string clientId = Console.ReadLine();


        var client = new HybridP2PClient(serverIp, 11000, clientId);

        client.Start();

    }

}


方案特点与优势

1. 混合连接策略

  • 智能连接选择:优先尝试TCP连接,失败后自动回退到UDP

  • 双协议支持:同时维护TCP和UDP端点信息

  • 连接冗余:增加P2P连接成功概率

2. 连接流程优化

客户端启动时:

  • 通过TCP连接到协调服务器

  • 通过UDP向服务器注册UDP端点

  • 启动TCP和UDP本地监听

连接请求时:

  • 请求方通过TCP发送连接请求

  • 服务器交换双方TCP/UDP端点信息

  • 双方同时尝试连接对方

连接建立:

  • 优先尝试TCP直连

  • TCP失败后尝试UDP打洞

  • 成功建立连接后进入通信模式

3. 技术亮点

  • TCP/UDP双监听:客户端同时监听TCP和UDP端口

  • 端点信息交换:服务器交换完整的网络端点信息

  • 连接状态管理:完善的连接状态跟踪和错误处理

  • 资源清理:程序退出时正确释放所有网络资源

4. 使用说明

启动协调服务器:

CoordinatorServer.exe

启动客户端:

HybridP2PClient.exe

> 输入协调服务器IP: [服务器公网IP]

> 输入客户端ID: [唯一ID]

建立连接:

  • 客户端A和B分别启动并注册

  • 在客户端A输入客户端B的ID

  • 系统自动尝试TCP连接

  • 如果TCP失败,自动尝试UDP打洞

  • 连接成功后即可开始通信

5. 网络穿透成功率优化

技术穿透成功率适用场景
TCP直连70-80%大多数家用路由器
UDP打洞85-95%企业级NAT设备
混合策略95%+各种网络环境

6. 增强功能建议

连接质量监测

private void MonitorConnectionQuality()

{

    while (_isConnected)

    {

        if (_useTCP)

        {

            // 检查TCP连接状态

            if (_tcpPeerConnection.Client.Poll(0, SelectMode.SelectRead))

            {

                byte[] buff = new byte[1];

                if (_tcpPeerConnection.Client.Receive(buff, SocketFlags.Peek) == 0)

                {

                    Console.WriteLine("TCP连接已断开");

                    _isConnected = false;

                    return;

                }

            }

        }

        else

        {

            // 发送UDP心跳包

            try

            {

                _udpClient.Send(new byte[1], 1, _peerUdpEP);

            }

            catch

            {

                Console.WriteLine("UDP连接已断开");

                _isConnected = false;

                return;

            }

        }

        

        Thread.Sleep(5000);

    }

}

连接日志记录

private void LogConnectionEvent(string eventType)

{

    string log = $"{DateTime.Now:HH:mm:ss} [{eventType}] " +

                 $"Client: {_clientId}, " +

                 $"Protocol: {(_useTCP ? "TCP" : "UDP")}, " +

                 $"Peer: {(_useTCP ? _peerTcpEP.ToString() : _peerUdpEP.ToString())}";

    

    File.AppendAllText("p2p_connection.log", log + Environment.NewLine);

    Console.WriteLine(log);

}

对称NAT检测

private bool DetectSymmetricNAT()

{

    try

    {

        // 发送UDP包到已知服务器

        var testEP = new IPEndPoint(IPAddress.Parse("8.8.8.8"), 53);

        _udpClient.Send(new byte[1], 1, testEP);

        

        // 检查本地端口是否变化

        var localEP = (IPEndPoint)_udpClient.Client.LocalEndPoint;

        Thread.Sleep(100);

        var newLocalEP = (IPEndPoint)_udpClient.Client.LocalEndPoint;

        

        return localEP.Port != newLocalEP.Port;

    }

    catch

    {

        return false;

    }

}

这个混合方案在绝大多数网络环境下都能成功建立P2P连接,通过智能选择最佳连接方式,提供了高可靠性的P2P通信能力。


服务器架构设计

1. 双协议处理架构​

2. 核心功能模块

模块功能协议关键数据结构
TCP监听器接受客户端TCP连接TCPTcpListener
UDP监听器接收客户端UDP数据包UDPUdpClient
注册管理器管理客户端端点信息TCP/UDP_tcpEndpoints_udpEndpoints
连接处理器处理P2P连接请求TCPHandleConnectionRequest
端点交换器交换客户端端点信息TCPSendTcpResponse
心跳检测器维护客户端连接状态UDPHEARTBEAT处理

3. 数据存储结构

// 存储客户端的TCP端点信息

private readonly Dictionary<string, IPEndPoint> _tcpEndpoints = new();


// 存储客户端的UDP端点信息

private readonly Dictionary<string, IPEndPoint> _udpEndpoints = new();


// 存储活跃的TCP连接

private readonly Dictionary<string, TcpClient> _tcpConnections = new();

服务器工作流程

启动服务器:

  • 同时监听TCP和UDP端口

  • 启动TCP和UDP监听线程

  1. 客户端注册:

    • 客户端通过TCP连接发送REGISTER:{clientId}

    • 客户端通过UDP发送REGISTER_UDP:{clientId}

    • 服务器记录TCP/UDP端点信息

  2. 连接请求:

    • 客户端A发送CONNECT:{clientA}:{clientB}

    • 服务器验证双方端点信息

    • 交换双方TCP和UDP端点信息

  3. 端点交换:

    • 发送给A: PEER_INFO:{clientB}:{tcpEp}:{udpEp}

    • 发送给B: PEER_INFO:{clientA}:{tcpEp}:{udpEp}

  4. 维护连接:

    • UDP心跳检测(HEARTBEAT)

    • 自动清理断开连接的客户端

部署指南

1. 服务器要求

  • 公网IP地址

  • 开放TCP/UDP端口11000(或自定义端口)

  • .NET 5+ 运行时环境

2. 启动服务器

HybridCoordinatorServer.exe

3. 客户端配置

客户端需要同时实现TCP和UDP连接逻辑,向服务器发送以下命令:

TCP注册:

// 连接到服务器TCP端口

TcpClient tcpClient = new TcpClient(serverIp, serverPort);

// 发送注册消息

string registerMsg = $"REGISTER:{clientId}";

byte[] data = Encoding.ASCII.GetBytes(registerMsg);

stream.Write(data, 0, data.Length);

UDP注册:

// 创建UDP客户端

UdpClient udpClient = new UdpClient(0);

// 发送UDP注册

string udpRegister = $"REGISTER_UDP:{clientId}";

byte[] udpData = Encoding.ASCII.GetBytes(udpRegister);

udpClient.Send(udpData, udpData.Length, serverEP);

4. 防火墙配置

确保服务器防火墙允许以下通信:

  • 入站TCP端口11000

  • 入站UDP端口11000

  • 客户端间通信端口(动态)

高级功能

1、NAT类型检测

public string DetectNatType(string clientId)

{

    lock (_lock)

    {

        if (!_tcpEndpoints.ContainsKey(clientId) return "Unknown";

        

        var tcpEp = _tcpEndpoints[clientId];

        var udpEp = _udpEndpoints.ContainsKey(clientId) ? _udpEndpoints[clientId] : null;

        

        if (udpEp == null) return "UDP未注册";

        

        // 检测端口变化

        bool samePort = tcpEp.Port == udpEp.Port;

        bool portIncrement = Math.Abs(tcpEp.Port - udpEp.Port) < 100;

        

        if (samePort) return "Full Cone NAT";

        if (portIncrement) return "Restricted Cone NAT";

        return "Symmetric NAT";

    }

}

2. 连接质量监控

private void MonitorConnectionQuality()

{

    while (true)

    {

        Thread.Sleep(30000); // 每30秒检查一次

        

        lock (_lock)

        {

            Console.WriteLine($"[状态] 当前连接数: {_tcpConnections.Count} TCP, {_udpEndpoints.Count} UDP");

            

            // 检测不活跃的UDP客户端

            List<string> toRemove = new List<string>();

            foreach (var kvp in _udpEndpoints)

            {

                try

                {

                    SendUdpResponse(kvp.Value, "PING");

                }

                catch

                {

                    toRemove.Add(kvp.Key);

                }

            }

            

            foreach (string clientId in toRemove)

            {

                _udpEndpoints.Remove(clientId);

                Console.WriteLine($"[清理] 移除不活跃UDP客户端: {clientId}");

            }

        }

    }

}

3. 安全增强

private bool AuthenticateClient(string clientId, IPEndPoint ep)

{

    // 简单的基于IP的认证

    string clientIp = ep.Address.ToString();

    

    // 在实际应用中应从数据库或配置文件读取

    Dictionary<string, string> allowedClients = new()

    {

        {"client1", "192.168.1.100"},

        {"client2", "10.0.0.5"}

    };

    

    if (allowedClients.TryGetValue(clientId, out string allowedIp))

    {

        return clientIp == allowedIp;

    }

    

    return false;

}

性能优化建议

  1. 连接池管理:

private void OptimizeTcpConnections()

{

    lock (_lock)

    {

        foreach (var client in _tcpConnections.Values)

        {

            // 设置TCP keep-alive

            client.Client.SetSocketOption(

                SocketOptionLevel.Socket, 

                SocketOptionName.KeepAlive, 

                true);

            

            // 设置更短的keep-alive间隔

            byte[] keepAliveValues = new byte[12];

            BitConverter.GetBytes(1u).CopyTo(keepAliveValues, 0);

            BitConverter.GetBytes(30000u).CopyTo(keepAliveValues, 4); // 30秒

            BitConverter.GetBytes(5000u).CopyTo(keepAliveValues, 8);  // 5秒重试

            client.Client.IOControl(IOControlCode.KeepAliveValues, keepAliveValues, null);

        }

    }

}

  1. 负载均衡:

public IPEndPoint GetOptimalEndpoint(string clientId)

{

    // 根据地理位置选择最佳服务器

    var clientEp = _tcpEndpoints[clientId];

    var serverLocations = new Dictionary<string, IPEndPoint>

    {

        {"US", new IPEndPoint(IPAddress.Parse("104.16.1.1"), 11000)},

        {"EU", new IPEndPoint(IPAddress.Parse("172.67.68.1"), 11000)},

        {"ASIA", new IPEndPoint(IPAddress.Parse("172.67.69.1"), 11000)}

    };

    

    // 简单的地理位置判断(实际应使用GeoIP数据库)

    byte firstByte = clientEp.Address.GetAddressBytes()[0];

    string region = firstByte < 80 ? "ASIA" : 

                  (firstByte < 160 ? "EU" : "US");

    

    return serverLocations[region];

}

  1. 日志记录:

private void LogEvent(string eventType, string clientId = null, string details = null)

{

    string logEntry = $"{DateTime.UtcNow:o}|{eventType}|{clientId ?? "SYSTEM"}|{details ?? ""}";

    

    // 写入文件

    File.AppendAllText("server_log.txt", logEntry + Environment.NewLine);

    

    // 发送到远程日志系统

    if (eventType == "ERROR" || eventType == "WARN")

    {

        SendToRemoteLog(logEntry);

    }

}

其它补充

1. 协议统一

组件消息类型客户端发送服务器响应修正说明
TCP注册客户端→服务器REGISTER:{clientId}REGISTERED保持原始格式
UDP端口注册服务器→客户端REQUEST_UDPUDP_PORT:{clientId}:{port}新增服务器主动请求机制
连接请求客户端→服务器CONNECT:{fromId}:{toId}PEER_INFO:{peerId}:{tcpEp}:{udpEp}保持原始格式
UDP心跳客户端→服务器HEARTBEAT:{clientId}HEARTBEAT_ACK保持原始格式
UDP打洞客户端→客户端PUNCH:{clientId}:{seq}-保持原始格式

2. 端口管理优化

  1. 服务器端:

    • 主动请求客户端UDP端口信息

    • 同时记录TCP连接来源端口和UDP端口

    • 定期更新UDP端点(处理NAT变化)

  2. 客户端:

    • 启动时自动获取UDP监听端口

    • 响应服务器的UDP端口请求

    • 定期发送UDP心跳保持NAT映射

3. 连接流程修正

  1. 注册流程:

连接流程:


4. 错误处理增强

服务器端:

  • 定期检测TCP连接状态

  • 自动清理断开的客户端

  • 详细的错误响应消息

客户端:

  • 完善的异常捕获

  • 连接超时处理

  • 网络状态监控

  • 部署测试指南

1. 服务器部署

# 编译服务器

csc HybridCoordinatorServer.cs


# 运行服务器

HybridCoordinatorServer.exe

2. 客户端运行

# 编译客户端

csc HybridP2PClient.cs


# 运行客户端

HybridP2PClient.exe

> 输入协调服务器IP: [服务器公网IP]

> 输入客户端ID: Client1

3. 测试流程

  • 在两个不同网络环境的机器上运行客户端

  • 在Client1控制台输入Client2的ID

  • 观察连接过程:

    • 先尝试TCP直连

    • TCP失败后自动尝试UDP打洞

    • 连接成功后可以聊天

4. 网络要求

组件协议端口方向说明
服务器TCP11000入站客户端注册连接
服务器UDP11000入站心跳和UDP注册
客户端TCP动态入站P2P TCP连接
客户端UDP动态入站P2P UDP通信

调试技巧

  1. 服务器日志:

    • 注册信息:记录客户端的TCP/UDP端点

    • 连接请求:跟踪端点交换过程

    • 心跳监控:显示活跃客户端数量

  2. 客户端调试:

// 添加调试输出

Console.WriteLine($"[DEBUG] 正在连接到 {endpoint}");

  1. 网络工具:

    • Wireshark:捕获分析网络包

    • TCPView:查看TCP连接状态

    • Netstat:检查端口监听情况

这个修正后的方案确保了客户端和服务器端之间的协议完全匹配,解决了之前存在的命名和消息格式不一致问题,同时增强了连接稳定性和错误处理能力。


该文章在 2025/6/24 9:57:01 编辑过

全部评论1

admin
2025年6月24日 10:36
相关教程:
【C#】利用现有的Windows远程桌面功能,通过P2P连接直接访问对方的远程桌面服务[15]
  http://31816.oa22.cn

该评论在 2025/6/24 10:36:31 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved