非同步方法配對表 (APM: Asynchronous Programming Model)
| 操作 | Begin 方法 | End 方法 | 說明 |
| 接受連線 | BeginAccept | EndAccept | 伺服器端建立新 Socket |
| 連線伺服器 | BeginConnect | EndConnect | 用戶端發起連線 |
| 斷開連線 | BeginDisconnect | EndDisconnect | 優雅關閉 |
| 接收資料 | BeginReceive | EndReceive | 非同步讀取 |
| 從指定位址接收 | BeginReceiveFrom | EndReceiveFrom | UDP 適用 |
| 傳送資料 | BeginSend | EndSend | 非同步寫入 |
| 傳送檔案 | BeginSendFile | EndSendFile | 高效檔案傳輸 |
| 傳送至指定位址 | BeginSendTo | EndSendTo | UDP 適用 |
核心機制
- 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)**,程式碼更簡潔、安全、效能更優。