Windows Sockets(Winsock)從 Berkeley Sockets 延伸而來,針對 Windows 訊息驅動架構新增了非同步選擇機制、阻塞處理鉤子等關鍵擴充。本文深入剖析 Winsock 1.1 的五大擴充特性、核心程式設計流程,並以實際程式碼示範從啟動到終止的完整生命週期管理。
WSOCK32.LIB)、標頭檔(WINSOCK.H)。WSOCK32.DLL(16/32 位元皆有),實作所有 API。將套接字網路事件(可讀、可寫、連線完成、關閉等)映射為視窗訊息,完美融入 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) = 錯誤碼。
gethostbyname/getservbyname 等為阻塞呼叫。WSAAsyncGetHostByName、WSAAsyncGetHostByAddr、WSAAsyncGetServByName、WSAAsyncGetServByPort。HANDLE,完成時發送視窗訊息,結果需呼叫 WSAGetAsyncBuf 取得。BlockingHook(),內部 GetMessage/DispatchMessage 讓出 CPU。WSASetBlockingHook(MyHook),恢復預設:WSACancelBlockingHook()(即 WSUnhookBlockingHook)。WSACancelBlockingCall() 取消當前阻塞操作。WSAGetLastError() — 取得呼叫執行緒最近錯誤碼WSASetLastError(err) — 設定錯誤碼errno 衝突。int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
int WSACleanup(void);
// wVersionRequested: MAKEWORD(2, 2) 請求 2.2 版
// lpWSAData: 回填實際版本、廠商資訊、最大套接字數等
// 必須成對呼叫,每次 WSAStartup 需對應一次 WSACleanup
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;
}
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; }
// 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
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);
}
void Cleanup() {
// 關閉所有套接字
for (auto s : openSockets) closesocket(s);
WSACleanup(); // 釋放 DLL 資源
}
| 特性 | Winsock 1.1 | Winsock 2.x |
|---|---|---|
| 協定支援 | 僅 TCP/IP (AF_INET) | 多協定 (IPX/SPX, AppleTalk, ATM...) |
| 非同步模型 | WSAAsyncSelect (視窗訊息) | WSAEventSelect (事件物件) + Overlapped I/O + IOCP |
| 多點傳送 | 不支援 | 完整 IGMP/多點傳送 API |
| QoS/流量控制 | 無 | GQoS API |
| DLL | WSOCK32.DLL | WS2_32.DLL |