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

深入 CSocket 編程:阻塞與非阻塞模式全解析

     編輯:關於VC++

前言

在 Windows 網路程式設計中,MFC 的 CSocket 類封裝了 Windows Sockets API,極大地簡化了網路通訊的開發難度。然而,要真正掌握其核心機制,深入理解阻塞與非阻塞兩種運作模式至關重要。本文將結合原始碼追蹤,詳細剖析這兩種模式在伺服器端的實現原理、訊息機制及適用場景。

核心基礎:網路傳輸服務提供者、Socket 事件與 Socket Window

在探討具體模式前,必須先釐清三個核心概念:

  • 網路傳輸服務提供者:以 ws2_32.dll 形式存在,由 svchost.exe 載入。當呼叫 socket() API 時,傳遞的地址家族、類型、協定參數決定由哪種提供者啟動服務。所有網路通訊實際上均由其完成。
  • Socket 事件:客戶端與伺服器通訊時觸發。關鍵事件包括 FD_CONNECT(客戶端連線事件)與 FD_ACCEPT(伺服器接收連線事件)。網路傳輸服務進程將事件存入 Socket 事件佇列,並向 Socket Window 發送 WM_SOCKET_NOTIFY 訊息。
  • Socket Window:呼叫 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 被阻塞。因此主執行緒會卡在循環中,直到有連線請求到達。

架構啟示

  • 阻塞模式必須在獨立工作執行緒中運行,否則會凍結主 UI 執行緒。
  • 適合單一或少量長連線場景,邏輯直觀,程式碼簡潔。

二、非阻塞模式:事件驅動的異步機制

非阻塞模式利用 Socket 事件訊息機制,實現異步通訊,無需額外執行緒。

實現步驟

  1. 從 CSocket 派生新類(如 CMySocket)。
  2. 重寫虛擬函數 OnAccept、OnReceive、OnClose 等事件處理器。
  3. 在事件處理器中編寫收發邏輯。

事件觸發鏈路追蹤:以 FD_ACCEPT 為例

  1. 客戶端發起連線,伺服器端網路傳輸服務進程觸發 FD_ACCEPT,存入事件佇列,並向 CSocketWnd 發送 WM_SOCKET_NOTIFY。
  2. CSocketWnd::OnSocketNotify 接收訊息,呼叫靜態函數 CSocket::AuxQueueAdd 入佇,再呼叫 CSocket::ProcessAuxQueue 處理。
  3. ProcessAuxQueue 透過 CAsyncSocket::LookupHandle 從映射表 m_pmapSocketHandle 找到對應的 CAsyncSocket* 指標。
  4. 解析 lParam 取得事件類型,switch 判斷為 FD_ACCEPT 後,呼叫 pSocket->OnAccept()。

技術亮點:CSocketWnd 作為 CSocket 的友元類,可存取其私有靜態成員,實現了從全域訊息泵到具體 Socket 實例的精準分發。

三、總結與選型建議

維度阻塞模式非阻塞模式
程式設計模型同步/循環等待異步/事件驅動
執行緒需求需獨立工作執行緒可在主執行緒/UI 執行緒運行
並發處理單執行緒難以支援多客戶端天然支援多客戶端並發
程式碼複雜度低,邏輯線性中,需派生類並重寫虛擬函數
適用場景簡單文件傳輸、單連線服務聊天室、即時通訊、高並發伺服器

核心結論:單一長連線、邏輯簡單時首選阻塞模式(配合工作執行緒);面對多客戶端、需即時響應、UI 整合的場景,非阻塞模式配合 CSocketWnd 訊息池機制是更優雅、擴展性更強的選擇。

參考資源

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