LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

C#實現混合P2P點對點直接連接方案(TCP + UDP),如果TCP連接失敗則改用UDP連接

admin
2025年6月23日 17:25 本文熱度 211

下面是一個整合了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

黄频国产免费高清视频,久久不卡精品中文字幕一区,激情五月天AV电影在线观看,欧美国产韩国日本一区二区
亚洲无线码在线一区 | 婷婷中文字幕精品一区二区 | 亚洲伊人久久综合影院五月 | 亚洲综合在线日韩欧美 | 在线观看国产高潮视频 | 欧洲日韩一区二区三区 |