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

Socket 非同步操作方法完整解析 (APM 模式)

     編輯:關於C#

非同步方法配對表 (APM: Asynchronous Programming Model)

操作Begin 方法End 方法說明
接受連線BeginAcceptEndAccept伺服器端建立新 Socket
連線伺服器BeginConnectEndConnect用戶端發起連線
斷開連線BeginDisconnectEndDisconnect優雅關閉
接收資料BeginReceiveEndReceive非同步讀取
從指定位址接收BeginReceiveFromEndReceiveFromUDP 適用
傳送資料BeginSendEndSend非同步寫入
傳送檔案BeginSendFileEndSendFile高效檔案傳輸
傳送至指定位址BeginSendToEndSendToUDP 適用

核心機制

  • BeginXXX:啟動非同步操作,傳入 AsyncCallback 回調與狀態物件,立即回傳 IAsyncResult。
  • EndXXX:在回調中呼叫,傳入 IAsyncResult,取得結果/例外,**必須呼叫**以釋放資源。
  • 執行緒池:完成通知在 ThreadPool 執行緒觸發回調。

範例:非同步 TCP 伺服器接受連線


private Socket _listener;
private readonly AsyncCallback _acceptCallback;

public void Start(int port)
{
    _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    _listener.Bind(new IPEndPoint(IPAddress.Any, port));
    _listener.Listen(100);
    _acceptCallback = AcceptCallback;
    _listener.BeginAccept(_acceptCallback, _listener);
}

private void AcceptCallback(IAsyncResult ar)
{
    Socket listener = (Socket)ar.AsyncState;
    Socket client = null;
    try
    {
        client = listener.EndAccept(ar); // 取得客戶端 Socket
        Console.WriteLine($"Client connected: {client.RemoteEndPoint}");
        // 繼續接受下一個連線
        listener.BeginAccept(_acceptCallback, listener);
        // 處理客戶端通訊 (啟動 BeginReceive...)
        HandleClient(client);
    }
    catch (ObjectDisposedException) { /* listener 已關閉 */ }
    catch (Exception ex) { Console.WriteLine($"Accept error: {ex.Message}"); }
}

非同步連線 (用戶端)


private Socket _client;
public void Connect(string host, int port)
{
    _client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    _client.BeginConnect(host, port, ConnectCallback, _client);
}

private void ConnectCallback(IAsyncResult ar)
{
    try
    {
        _client.EndConnect(ar);
        Console.WriteLine("Connected");
        // 開始接收
        _client.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceiveCallback, _client);
    }
    catch (Exception ex) { Console.WriteLine($"Connect failed: {ex.Message}"); }
}

非同步收發資料


private readonly byte[] _buffer = new byte[4096];

private void ReceiveCallback(IAsyncResult ar)
{
    Socket client = (Socket)ar.AsyncState;
    try
    {
        int received = client.EndReceive(ar);
        if (received > 0)
        {
            string msg = Encoding.UTF8.GetString(_buffer, 0, received);
            Console.WriteLine($"Received: {msg}");
            // 繼續接收下一筆
            client.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceiveCallback, client);
        }
        else { client.Close(); } // 連線關閉
    }
    catch (ObjectDisposedException) { }
    catch (Exception ex) { Console.WriteLine($"Receive error: {ex.Message}"); client.Close(); }
}

public void Send(string message)
{
    byte[] data = Encoding.UTF8.GetBytes(message);
    _client.BeginSend(data, 0, data.Length, SocketFlags.None, SendCallback, _client);
}

private void SendCallback(IAsyncResult ar)
{
    try { _client.EndSend(ar); }
    catch (Exception ex) { Console.WriteLine($"Send error: {ex.Message}"); }
}

APM vs TAP (Task-based Asynchronous Pattern) 現代建議

特性APM (Begin/End)TAP (Async/Await)
程式碼可讀性回調地獄、錯誤處理分散線性邏輯、try/catch 集中
組合性困難Task.WhenAll, WhenAny, ContinueWith
取消支援手動實作CancellationToken 內建
效能基礎狀態機優化、ValueTask 選項
適用版本.NET Framework 1.0+.NET 4.5+ / .NET Core / .NET 5+

現代 Socket 非同步寫法 (TAP)


public async Task AcceptAsync(CancellationToken ct = default)
{
    return await _listener.AcceptAsync(ct); // .NET Core 2.1+ / .NET 5+
}

public async Task ConnectAsync(string host, int port, CancellationToken ct = default)
{
    await _client.ConnectAsync(host, port, ct);
}

public async Task ReceiveAsync(Memory buffer, CancellationToken ct = default)
{
    return await _client.ReceiveAsync(buffer, SocketFlags.None, ct);
}

public async Task SendAsync(ReadOnlyMemory data, CancellationToken ct = default)
{
    await _client.SendAsync(data, SocketFlags.None, ct);
}

結論:理解 APM 有助於維護舊代碼,**新專案請全面採用 TAP (async/await + Memory<byte>/ValueTask)**,程式碼更簡潔、安全、效能更優。

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