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

Windows Sockets 1.1 程式設計精要:非同步選擇機制、阻塞鉤子與啟動終止流程

     編輯:關於C語言

Windows Sockets(Winsock)從 Berkeley Sockets 延伸而來,針對 Windows 訊息驅動架構新增了非同步選擇機制、阻塞處理鉤子等關鍵擴充。本文深入剖析 Winsock 1.1 的五大擴充特性、核心程式設計流程,並以實際程式碼示範從啟動到終止的完整生命週期管理。

Winsock 1.1 兩大組件

  • 開發組件:規範文件、匯入庫(WSOCK32.LIB)、標頭檔(WINSOCK.H)。
  • 執行組件:WSOCK32.DLL(16/32 位元皆有),實作所有 API。

五大核心擴充特性

1. 非同步選擇機制 — WSAAsyncSelect

將套接字網路事件(可讀、可寫、連線完成、關閉等)映射為視窗訊息,完美融入 Windows 事件迴圈。

int WSAAsyncSelect(SOCKET s, HWND hWnd, u_int wMsg, long lEvent);
// hWnd: 接收訊息的視窗
// wMsg: 自訂訊息 (WM_USER + x)
// lEvent: 關注事件位元組合
//   FD_READ      - 可讀
//   FD_WRITE     - 可寫
//   FD_OOB       - 帶外資料
//   FD_ACCEPT    - 新連線到達
//   FD_CONNECT   - 連線完成
//   FD_CLOSE     - 連線關閉

// 範例:關注讀/寫/連線/關閉
rc = WSAAsyncSelect(s, hWnd, WM_SOCKET_NOTIFY, FD_READ|FD_WRITE|FD_CONNECT|FD_CLOSE);

// 註銷:lEvent = 0
WSAAsyncSelect(s, hWnd, 0, 0);

訊息處理:wParam = 套接字,LOWORD(lParam) = 事件,HIWORD(lParam) = 錯誤碼。

2. 非同步請求函式 — WSAAsyncGetXByY

  • Berkeley Sockets 的 gethostbyname/getservbyname 等為阻塞呼叫。
  • Winsock 提供非同步對應:WSAAsyncGetHostByName、WSAAsyncGetHostByAddr、WSAAsyncGetServByName、WSAAsyncGetServByPort。
  • 回傳 HANDLE,完成時發送視窗訊息,結果需呼叫 WSAGetAsyncBuf 取得。

3. 阻塞處理方法 — Blocking Hook

  • Windows 非搶佔式多任務下,阻塞呼叫會凍結整個系統。
  • Winsock 在阻塞時進入 BlockingHook(),內部 GetMessage/DispatchMessage 讓出 CPU。
  • 可自訂:WSASetBlockingHook(MyHook),恢復預設:WSACancelBlockingHook()(即 WSUnhookBlockingHook)。
  • 鉤子中禁用其他 Winsock API,僅可呼叫 WSACancelBlockingCall() 取消當前阻塞操作。
  • 現代 NT 核心(WinNT/2K/XP+)採搶佔式排程,阻塞不再凍結系統,但為相容性保留機制。

4. 錯誤處理 — 執行緒區域儲存

  • 為相容多執行緒(Windows/Unix),提供:
  • WSAGetLastError() — 取得呼叫執行緒最近錯誤碼
  • WSASetLastError(err) — 設定錯誤碼
  • 避免全域 errno 衝突。

5. 啟動與終止 — WSAStartup / WSACleanup

int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
int WSACleanup(void);

// wVersionRequested: MAKEWORD(2, 2) 請求 2.2 版
// lpWSAData: 回填實際版本、廠商資訊、最大套接字數等
// 必須成對呼叫,每次 WSAStartup 需對應一次 WSACleanup

核心程式設計流程

1. 啟動 Winsock

WSADATA wsaData;
WORD wVersion = MAKEWORD(2, 2); // 請求 2.2
int err = WSAStartup(wVersion, &wsaData);
if (err != 0) { /* 載入失敗 */ }
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
    WSACleanup(); // 版本不符
    return FALSE;
}

2. 建立套接字並註冊非同步事件

SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET) { WSACleanup(); return FALSE; }

rc = WSAAsyncSelect(s, hWnd, WM_SOCKET_NOTIFY, FD_READ|FD_WRITE|FD_CONNECT|FD_CLOSE);
if (rc == SOCKET_ERROR) { closesocket(s); WSACleanup(); return FALSE; }

3. 連線/監聽

// Client
sockaddr_in server = {AF_INET, htons(80), inet_addr("1.2.3.4")};
connect(s, (sockaddr*)&server, sizeof(server)); // 非同步,稍後收到 FD_CONNECT

// Server
bind(s, (sockaddr*)&local, sizeof(local));
listen(s, SOMAXCONN); // 稍後收到 FD_ACCEPT

4. 視窗訊息處理迴圈

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    if (msg == WM_SOCKET_NOTIFY) {
        SOCKET s = (SOCKET)wParam;
        int event = LOWORD(lParam);
        int error = HIWORD(lParam);
        if (error) { /* 處理錯誤 */ }

        switch (event) {
            case FD_CONNECT:  OnConnect(s); break;
            case FD_ACCEPT:   OnAccept(s); break;
            case FD_READ:     OnRead(s); break;
            case FD_WRITE:    OnWrite(s); break;
            case FD_CLOSE:    OnClose(s); break;
        }
        return 0;
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

5. 終止清理

void Cleanup() {
    // 關閉所有套接字
    for (auto s : openSockets) closesocket(s);
    WSACleanup(); // 釋放 DLL 資源
}

Winsock 1.1 vs 2.x 關鍵差異

總結

Winsock 1.1 以 WSAAsyncSelect 視窗訊息機制完美適配 Windows 3.x/9x 事件驅動模型,雖已被 Winsock 2.x 取代,但其「非同步選擇 → 訊息分派 → 事件處理」的程式設計範例,對理解現代 WSAEventSelect/IOCP 事件驅動架構極具啟發意義。掌握啟動/終止配對、阻塞鉤子限制、錯誤碼執行緒區域化等核心規範,是編寫健壯網路程式的基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
特性Winsock 1.1Winsock 2.x
協定支援僅 TCP/IP (AF_INET)多協定 (IPX/SPX, AppleTalk, ATM...)
非同步模型WSAAsyncSelect (視窗訊息)WSAEventSelect (事件物件) + Overlapped I/O + IOCP
多點傳送不支援完整 IGMP/多點傳送 API
QoS/流量控制無GQoS API
DLLWSOCK32.DLLWS2_32.DLL