首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# Socket 程式設計完整筆記:TCP/UDP 同步非同步、非阻塞模式實戰

     編輯:關於C#

Socket 是網路程式設計的基石。.NET 提供 System.Net.Sockets.Socket 類別封裝 Berkeley Socket API,並提供 TcpClient/TcpListener/UdpClient 高階封裝。本文系統整理 Socket 核心概念、同步/非同步模式、TCP/UDP 差異、完整可執行範例,並探討實務選型建議。

核心概念

概念說明
Socket網路通訊端點,IP + Port + 協定 的三元組
TCP (SOCK_STREAM)面向連接、可靠、有序、雙向串流、三向交握建立連線
UDP (SOCK_DGRAM)無連接、不可靠、無序、資料報文、低延遲、適合廣播/即時
阻塞模式預設,Accept/Receive/Send 等待完成才返回
非阻塞/非同步Begin/End 模式、Task-based (SocketAsyncEventArgs)、IOCP 效能最佳

TCP 同步伺服器/客戶端完整範例

TcpServer.cs


using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace TcpSyncDemo
{
    class TcpServer
    {
        static void Main()
        {
            const int PORT = 9050;
            var localEP = new IPEndPoint(IPAddress.Any, PORT);
            var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            
            server.Bind(localEP);
            server.Listen(10);
            Console.WriteLine($"TCP Server listening on {localEP}...");

            while (true)
            {
                Socket client = server.Accept(); // 阻塞等待連線
                var clientEP = (IPEndPoint)client.RemoteEndPoint;
                Console.WriteLine($"Client connected: {clientEP}");

                // 歡迎訊息
                byte[] welcome = Encoding.ASCII.GetBytes("Welcome to TCP Server!\n");
                client.Send(welcome);

                // 回應迴圈
                byte[] buffer = new byte[1024];
                while (true)
                {
                    int received = client.Receive(buffer);
                    if (received == 0) break; // 客戶端斷開
                    
                    string msg = Encoding.ASCII.GetString(buffer, 0, received).Trim();
                    Console.WriteLine($"Received: {msg}");
                    
                    if (msg.Equals("exit", StringComparison.OrdinalIgnoreCase)) break;
                    
                    // 回送確認
                    client.Send(buffer, received, SocketFlags.None);
                }
                
                Console.WriteLine($"Client disconnected: {clientEP}");
                client.Shutdown(SocketShutdown.Both);
                client.Close();
            }
        }
    }
}

TcpClient.cs


using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace TcpSyncDemo
{
    class TcpClient
    {
        static void Main()
        {
            Console.Write("Server IP: ");
            string ip = Console.ReadLine();
            Console.Write("Server Port: ");
            int port = int.Parse(Console.ReadLine());

            var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            var serverEP = new IPEndPoint(IPAddress.Parse(ip), port);

            try
            {
                client.Connect(serverEP);
                Console.WriteLine("Connected to server.");

                byte[] buffer = new byte[1024];
                int recv = client.Receive(buffer);
                Console.WriteLine($"Server: {Encoding.ASCII.GetString(buffer, 0, recv)}");

                while (true)
                {
                    Console.Write("> ");
                    string input = Console.ReadLine();
                    if (input == "exit") break;

                    client.Send(Encoding.ASCII.GetBytes(input));
                    
                    recv = client.Receive(buffer);
                    Console.WriteLine($"Echo: {Encoding.ASCII.GetString(buffer, 0, recv)}");
                }
            }
            catch (SocketException ex)
            {
                Console.WriteLine($"Connection failed: {ex.Message}");
            }
            finally
            {
                client.Shutdown(SocketShutdown.Both);
                client.Close();
            }
        }
    }
}

UDP 同步伺服器/客戶端完整範例

UdpServer.cs


using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace UdpSyncDemo
{
    class UdpServer
    {
        static void Main()
        {
            const int PORT = 9050;
            var localEP = new IPEndPoint(IPAddress.Any, PORT);
            var server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            server.Bind(localEP);
            Console.WriteLine($"UDP Server listening on {localEP}...");

            EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
            byte[] buffer = new byte[1024];

            while (true)
            {
                int recv = server.ReceiveFrom(buffer, ref remoteEP);
                string msg = Encoding.ASCII.GetString(buffer, 0, recv);
                Console.WriteLine($"From {remoteEP}: {msg}");

                // 回送
                server.SendTo(buffer, recv, SocketFlags.None, remoteEP);
            }
        }
    }
}

UdpClient.cs


using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace UdpSyncDemo
{
    class UdpClient
    {
        static void Main()
        {
            var serverEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9050);
            var client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
            byte[] buffer = new byte[1024];

            // 首次發送
            client.SendTo(Encoding.ASCII.GetBytes("Hello Server"), serverEP);
            int recv = client.ReceiveFrom(buffer, ref remoteEP);
            Console.WriteLine($"Server: {Encoding.ASCII.GetString(buffer, 0, recv)}");

            while (true)
            {
                Console.Write("> ");
                string input = Console.ReadLine();
                if (input == "exit") break;

                client.SendTo(Encoding.ASCII.GetBytes(input), serverEP);
                recv = client.ReceiveFrom(buffer, ref remoteEP);
                Console.WriteLine($"Echo: {Encoding.ASCII.GetString(buffer, 0, recv)}");
            }
            client.Close();
        }
    }
}

非同步模式 (APM Begin/End) 關鍵模式


// 非同步 Accept
server.BeginAccept(AcceptCallback, server);

void AcceptCallback(IAsyncResult ar)
{
    var listener = (Socket)ar.AsyncState;
    Socket client = listener.EndAccept(ar);
    // 立即投遞下一個 Accept
    listener.BeginAccept(AcceptCallback, listener);
    
    // 開始非同步接收
    var state = new StateObject { Socket = client };
    client.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, ReceiveCallback, state);
}

void ReceiveCallback(IAsyncResult ar)
{
    var state = (StateObject)ar.AsyncState;
    Socket client = state.Socket;
    int read = client.EndReceive(ar);
    if (read > 0)
    {
        // 處理資料...
        client.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, ReceiveCallback, state);
    }
    else client.Close();
}

class StateObject { public Socket Socket; public byte[] Buffer = new byte[1024]; }

現代 Task-based 非同步 (SocketAsyncEventArgs / ValueTask)

.NET Core / .NET 5+ 建議使用 SocketAsyncEventArgs 搭配 ValueTask 或 Memory<byte> 實現高效能 IOCP 模式,避免 APM 的物件分配開銷。範例略,請參考 Microsoft 官方文件 SocketAsyncEventArgs。

高階封裝類別比較

類別協定特點適用場景
TcpClientTCP簡化 Connect/Send/Receive,內建 NetworkStream客戶端快速開發
TcpListenerTCP簡化 Bind/Listen/Accept,回傳 TcpClient伺服器快速開發
UdpClientUDP簡化 SendTo/ReceiveFrom,支援 MulticastUDP 快速開發
SocketTCP/UDP/Raw完整控制、非同步、IOCP、效能最佳高併發、自定義協定、極致效能

實務選型建議

  1. 可靠性優先、訊息順序重要:TCP (HTTP、資料庫、訊息佇列、檔案傳輸)
  2. 低延遲、容忍丟包、廣播/多播:UDP (即時遊戲、語音視訊、IoT 感測器、DNS、NTP)
  3. 高併發伺服器 (C10K+):Socket + SocketAsyncEventArgs + IOCP / Linux epoll
  4. 快速原型、維護性優先:TcpClient/TcpListener/UdpClient
  5. 穿透 NAT/防火牆:考慮 UDP 打洞、STUN/TURN、WebRTC

常見坑點與最佳實務

  • TCP 黏包/拆包:應用層必須定義訊息邊界 (長度前綴、分隔符、固定長度)
  • Send 不保證一次發送完畢:檢查回傳值,未發完需迴圈續送
  • Receive 回傳 0 表示對端關閉:非錯誤,正常斷開流程
  • 非同步回呼執行緒池:避免在回呼中執行耗時運算,投遞至工作佇列
  • IDisposable 模式:使用 using 或 try/finally 確保 Socket.Close() / Dispose()
  • 防火牆/防毒軟體:測試時暫時關閉或加入例外規則
  • IPv6 相容:使用 IPAddress.IPv6Any、Socket.OPTION_IPV6_V6ONLY = false

總結

掌握 Socket 同步/非同步模式、TCP/UDP 特性與適用場景,是 .NET 網路程式設計的核心竭力。從簡單的主控台聊天室到百萬併發的即時通訊伺服器,核心原理始終圍繞 端點建立、連線管理、資料收發、生命週期控制 四大面向。建議由高階封裝 (TcpClient) 入手,逐步深入 Socket 非同步與 IOCP,構建穩健高效的網路應用系統。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved