在 Windows 網路程式設計中,MFC 的 CSocket 類封裝了 Windows Sockets API,極大地簡化了網路通訊的開發難度。然而,要真正掌握其核心機制,深入理解阻塞與非阻塞兩種運作模式至關重要。本文將結合原始碼追蹤,詳細剖析這兩種模式在伺服器端的實現原理、訊息機制及適用場景。
在探討具體模式前,必須先釐清三個核心概念:
ws2_32.dll 形式存在,由 svchost.exe 載入。當呼叫 socket() API 時,傳遞的地址家族、類型、協定參數決定由哪種提供者啟動服務。所有網路通訊實際上均由其完成。FD_CONNECT(客戶端連線事件)與 FD_ACCEPT(伺服器接收連線事件)。網路傳輸服務進程將事件存入 Socket 事件佇列,並向 Socket Window 發送 WM_SOCKET_NOTIFY 訊息。CSocket::Create 時,內部會 new 一個 CSocketWnd 實例(基於 CWnd),作為所有 Socket 共享的訊息池,其視窗控制代碼儲存於模組狀態的 m_hSocketWindow 中。阻塞模式下,伺服器與客戶端通訊處於同步狀態。典型流程為:實例化 CSocket → Create() → Listen() → while(!Accept()) 循環等待連線。
CSocket::Accept 的阻塞循環while(!Accept(...))
{
if (GetLastError() == WSAEWOULDBLOCK)
PumpMessage(FD_ACCEPT);
else
return FALSE;
}
Accept 不斷檢查事件佇列中是否有 FD_ACCEPT 事件。若無,返回 0 並設錯誤碼 WSAEWOULDBLOCK,隨即呼叫 PumpMessage(FD_ACCEPT)。
關鍵點:PumpMessage 並非處理網路事件,而是維持 Socket Window 訊息泵活躍(如處理 WM_PAINT),但絕大多數 WM_SOCKET_NOTIFY 被阻塞。因此主執行緒會卡在循環中,直到有連線請求到達。
非阻塞模式利用 Socket 事件訊息機制,實現異步通訊,無需額外執行緒。
CSocket 派生新類(如 CMySocket)。OnAccept、OnReceive、OnClose 等事件處理器。FD_ACCEPT 為例FD_ACCEPT,存入事件佇列,並向 CSocketWnd 發送 WM_SOCKET_NOTIFY。CSocketWnd::OnSocketNotify 接收訊息,呼叫靜態函數 CSocket::AuxQueueAdd 入佇,再呼叫 CSocket::ProcessAuxQueue 處理。ProcessAuxQueue 透過 CAsyncSocket::LookupHandle 從映射表 m_pmapSocketHandle 找到對應的 CAsyncSocket* 指標。lParam 取得事件類型,switch 判斷為 FD_ACCEPT 後,呼叫 pSocket->OnAccept()。技術亮點:CSocketWnd 作為 CSocket 的友元類,可存取其私有靜態成員,實現了從全域訊息泵到具體 Socket 實例的精準分發。
| 維度 | 阻塞模式 | 非阻塞模式 |
|---|---|---|
| 程式設計模型 | 同步/循環等待 | 異步/事件驅動 |
| 執行緒需求 | 需獨立工作執行緒 | 可在主執行緒/UI 執行緒運行 |
| 並發處理 | 單執行緒難以支援多客戶端 | 天然支援多客戶端並發 |
| 程式碼複雜度 | 低,邏輯線性 | 中,需派生類並重寫虛擬函數 |
| 適用場景 | 簡單文件傳輸、單連線服務 | 聊天室、即時通訊、高並發伺服器 |
核心結論:單一長連線、邏輯簡單時首選阻塞模式(配合工作執行緒);面對多客戶端、需即時響應、UI 整合的場景,非阻塞模式配合
CSocketWnd訊息池機制是更優雅、擴展性更強的選擇。