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

C# Socket 通訊全景指南:同步、非同步、多線程與 TcpClient

     編輯:關於C語言

前言

Socket 是網路程式設計的基石。本文系統整理 C# 中同步/非同步 Socket、TcpListener/TcpClient 高階封裝、多線程模型、以及核心網路概念(協議、端點、位元組順序、流控等),附上可直接落地的伺服器/客戶端完整範本。

核心概念速查

概念說明
Socket網路通訊端點,封裝 IP + Port + 協議
AddressFamily.InterNetworkIPv4
SocketType.StreamTCP,可靠、有序、雙向位元組流
SocketType.DgramUDP,不可靠、無序、資料報
IPEndPointIP 位址 + 埠號的組合
協議端口傳輸層識別應用程式進程的邏輯地址

一、同步 Socket 伺服器/客戶端

伺服器端


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

public class SyncTcpServer
{
    private readonly Socket _listenSocket;
    private readonly Thread _listenThread;
    private bool _running;

    public SyncTcpServer(int port)
    {
        _listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _listenSocket.Bind(new IPEndPoint(IPAddress.Any, port));
        _listenSocket.Listen(100); // backlog
        _listenThread = new Thread(ListenLoop) { IsBackground = true };
    }

    public void Start() => _listenThread.Start();

    private void ListenLoop()
    {
        _running = true;
        while (_running)
        {
            try
            {
                Socket client = _listenSocket.Accept(); // 阻塞等待連線
                ThreadPool.QueueUserWorkItem(HandleClient, client); // 交給執行緒池處理
            }
            catch (SocketException) when (!_running) { break; }
        }
    }

    private void HandleClient(object state)
    {
        var client = (Socket)state;
        var buffer = new byte[4096];
        try
        {
            while (true)
            {
                int len = client.Receive(buffer);
                if (len == 0) break; // 客戶端正常關閉
                string msg = Encoding.UTF8.GetString(buffer, 0, len);
                Console.WriteLine($"收到:{msg}");
                // 回應
                client.Send(Encoding.UTF8.GetBytes($"Echo: {msg}"));
            }
        }
        catch (SocketException) { /* 連線中斷 */ }
        finally { client.Close(); }
    }

    public void Stop()
    {
        _running = false;
        _listenSocket.Close();
    }
}

客戶端


public class SyncTcpClient
{
    private readonly Socket _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    public void Connect(string ip, int port) => _socket.Connect(ip, port);

    public void Send(string msg) => _socket.Send(Encoding.UTF8.GetBytes(msg));

    public string Receive()
    {
        var buffer = new byte[4096];
        int len = _socket.Receive(buffer);
        return len > 0 ? Encoding.UTF8.GetString(buffer, 0, len) : null;
    }

    public void Close() => _socket.Close();
}

二、TcpListener / TcpClient 高階封裝(推薦)

內建 NetworkStream 支援 StreamReader/Writer,程式碼更簡潔。

伺服器


public class TcpServer
{
    private readonly TcpListener _listener;
    private readonly CancellationTokenSource _cts = new();

    public TcpServer(int port) => _listener = new TcpListener(IPAddress.Any, port);

    public async Task StartAsync()
    {
        _listener.Start();
        while (!_cts.Token.IsCancellationRequested)
        {
            TcpClient client = await _listener.AcceptTcpClientAsync();
            _ = Task.Run(() => HandleClientAsync(client), _cts.Token); // Fire-and-forget
        }
    }

    private async Task HandleClientAsync(TcpClient client)
    {
        using (client)
        using (var stream = client.GetStream())
        using (var reader = new StreamReader(stream, Encoding.UTF8))
        using (var writer = new StreamWriter(stream, Encoding.UTF8) { AutoFlush = true })
        {
            string line;
            while ((line = await reader.ReadLineAsync()) != null)
            {
                Console.WriteLine($"收到:{line}");
                await writer.WriteLineAsync($"Echo: {line}");
            }
        }
    }

    public void Stop() => _cts.Cancel();
}

三、非同步 Socket (APM / EAP / TAP)

現代 .NET 僅建議使用 **TAP (Task-based Asynchronous Pattern)**,即 AcceptAsync、ReceiveAsync、SendAsync 或搭配 NetworkStream 的 ReadAsync/WriteAsync。舊式 BeginAccept/EndAccept、SocketAsyncEventArgs 僅供維護舊專案參考。

SocketAsyncEventArgs 高效能模式(高併發伺服器專用)


// 物件池避免頻繁分配 SocketAsyncEventArgs
// 參考 Microsoft 官方範例:https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Sockets/tests/SocketAsyncEventArgsTests.cs

四、多線程模型與最佳實踐

模型適用場景優點缺點
每連線一執行緒低併發 (<100)程式碼簡單、阻塞式 API 直觀執行緒開銷大、記憶體壓力高
執行緒池 + 同步 Socket中併發 (100~1,000)利用 ThreadPool 自動調度阻塞呼叫佔用池執行緒,可能導致饑餓
非同步 I/O (TAP) + 完成端口高併發 (>10,000)少量執行緒處理大量連線、擴展性最佳程式碼複雜、錯誤處理繁瑣
SocketAsyncEventArgs極高併發、極低延遲零分配、完成端口原生支援API 困難、易出 Bug

五、網路程式設計關鍵細節

  • 位元組順序:網路序為 Big-Endian,跨平台傳輸 int/long 需用 IPAddress.HostToNetworkOrder / NetworkToHostOrder。
  • 訊息邊界:TCP 是位元組流,無訊息邊界。必須自訂協議:
    — 定長標頭 (如 4 bytes 長度) + 本體
    — 分隔符 (如 \n, \r\n)
    — 固定長度
  • Nagle 演算法:預設開啟,小封包會延遲合併。即時互動 (遊戲、SSH) 應設 socket.NoDelay = true。
  • Keep-Alive:偵測斷線,socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true)。
  • 優雅關閉:socket.Shutdown(SocketShutdown.Both) 後再 Close(),確保緩衝區資料送達。
  • 例外處理:SocketException.SocketErrorCode 區分 ConnectionReset、TimedOut、WouldBlock 等。

六、完整可執行範例:簡易聊天室伺服器


class ChatServer
{
    private readonly TcpListener _listener = new TcpListener(IPAddress.Any, 8888);
    private readonly List _clients = new();
    private readonly object _lock = new();

    public async Task RunAsync()
    {
        _listener.Start();
        Console.WriteLine("伺服器啟動於 8888...");
        while (true)
        {
            TcpClient client = await _listener.AcceptTcpClientAsync();
            _ = HandleClientAsync(client);
        }
    }

    private async Task HandleClientAsync(TcpClient client)
    {
        using (client)
        using (var stream = client.GetStream())
        using (var reader = new StreamReader(stream, Encoding.UTF8))
        using (var writer = new StreamWriter(stream, Encoding.UTF8) { AutoFlush = true })
        {
            lock (_lock) _clients.Add(writer);
            try
            {
                string msg;
                while ((msg = await reader.ReadLineAsync()) != null)
                {
                    Broadcast(msg, writer);
                }
            }
            finally
            {
                lock (_lock) _clients.Remove(writer);
            }
        }
    }

    private void Broadcast(string msg, StreamWriter sender)
    {
        lock (_lock)
        {
            foreach (var w in _clients)
            {
                if (w != sender) w.WriteLine(msg);
            }
        }
    }
}

總結

選擇正確的 Socket 程式設計模型取決於併發規模與團隊熟悉度:

  1. 入門/低併發:TcpListener/TcpClient + async/await + StreamReader/Writer。
  2. 中高併發:純 Socket + ReceiveAsync/SendAsync + Memory<byte> / ArrayPool。
  3. 極致效能:SocketAsyncEventArgs + 物件池 + IOCompletionPort 原生語意。

無論何種模型,掌握「訊息幀處理」、「背壓控制」、「連線生命週期管理」才是打造穩健網路服務的核心能力。

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