// 請求版本 2.2 (WinSock 2 標準)
WORD wVersionRequested = MAKEWORD(2, 2);
WSADATA wsaData;
int err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) { /* 載入失敗 */ }
// 檢查實際支援版本
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
WSACleanup();
return; // 版本不符
}
// ... 使用 Socket ...
WSACleanup(); // 程式結束前必須呼叫,釋放 DLL 資源
關鍵點:
wsaData.wHighVersion 為 DLL 最高支援版本,wVersion 為協商結果。WSAStartup 需對應多次 WSACleanup (引用計數)。
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // TCP
// 或
SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // UDP
if (s == INVALID_SOCKET) {
int err = WSAGetLastError();
// 處理錯誤
}
| 參數 | 常用值 | 說明 |
|---|---|---|
| af | AF_INET (IPv4), AF_INET6 (IPv6) | 位址家族 |
| type | SOCK_STREAM (TCP), SOCK_DGRAM (UDP), SOCK_RAW | Socket 類型 |
| protocol | IPPROTO_TCP, IPPROTO_UDP, 0 (自動) | 協議,通常 0 即可 |
Socket 描述符本質:進程私有的索引值,指向核心 Socket 物件。核心物件有引用計數,closesocket 遞減計數,歸零時釋放。
int ret = closesocket(s);
if (ret == SOCKET_ERROR) { /* WSAGetLastError() */ }
行為:
WSADuplicateSocket 或 fork 等) → 僅移除表項,遞減計數。
sockaddr_in serverAddr = {};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(8080);
inet_pton(AF_INET, "192.168.1.100", &serverAddr.sin_addr);
int ret = connect(s, (sockaddr*)&serverAddr, sizeof(serverAddr));
if (ret == SOCKET_ERROR) { /* WSAGetLastError: WSAECONNREFUSED, WSAETIMEDOUT... */ }
sockaddr_in localAddr = {};
localAddr.sin_family = AF_INET;
localAddr.sin_port = htons(8080);
localAddr.sin_addr.s_addr = INADDR_ANY; // 監聽所有介面
bind(s, (sockaddr*)&localAddr, sizeof(localAddr));
listen(s, SOMAXCONN); // 佇列長度
sockaddr_in clientAddr; int addrLen = sizeof(clientAddr);
SOCKET clientSocket = accept(s, (sockaddr*)&clientAddr, &addrLen);
// clientSocket 為新連線專用 Socket,原 s 繼續監聽
int send(SOCKET s, const char* buf, int len, int flags);
// flags: 0 通常; MSG_DONTROUTE, MSG_OOB (帶外資料)
SOCKET_ERROR,WSAGetLastError() == WSAECONNRESET。
int recv(SOCKET s, char* buf, int len, int flags);
// flags: 0 通常; MSG_PEEK (窺視不移除), MSG_OOB
int sendto(SOCKET s, const char* buf, int len, int flags,
const sockaddr* to, int tolen);
int recvfrom(SOCKET s, char* buf, int len, int flags,
sockaddr* from, int* fromlen);
recvfrom 返回發送端地址,可直接回覆。| 模型 | API | 適用場景 | 複雜度 |
|---|---|---|---|
| 阻塞 | 預設 | 簡單 Client、獨立執行緒 Server | 低 |
| 非阻塞 | ioctlsocket(FIONBIO) | 單執行緒輪詢、select 配合 | 中 |
| select | select() | 跨平台、連線數 < FD_SETSIZE (1024) | 中 |
| WSAAsyncSelect | WSAAsyncSelect(hWnd, msg) | MFC/Win32 GUI 應用,訊息驅動 | 低 (MFC 封裝) |
| WSAEventSelect | WSAEventSelect + WSAWaitForMultipleEvents | Console/Service、WaitForMultipleObjects 整合 | 中 |
| Overlapped I/O (IOCP) | WSASocket(..., WSA_FLAG_OVERLAPPED) + GetQueuedCompletionStatus | 高併發 Server (萬級連線)、效能極致 | 高 |
| 錯誤碼 | 含義 | 典型原因 |
|---|---|---|
| WSAEINTR | 阻塞呼叫被中斷 | WSACancelBlockingCall |
| WSAEACCES | 權限拒絕 | 嘗試綁定 < 1024 埠 (需管理員)、廣播未設 SO_BROADCAST |
| WSAEINVAL | 參數無效 | socket 狀態錯誤、buffer 為 NULL |
| WSAEMFILE | 描述符耗盡 | 未 close socket、FD_SETSIZE 限制 |
| WSAEWOULDBLOCK | 非阻塞下無資料/緩衝區滿 | 正常,需稍後重試或 select 等待 |
| WSAECONNRESET | 連線被對端重置 | 對端崩潰、防火牆截斷、RST 封包 |
| WSAETIMEDOUT | 連線逾時 | 網路不通、對端無回應 |
| WSAECONNREFUSED | 連線被拒絕 | Server 未 listen、埠號錯誤 |
WSAStartup(MAKEWORD(2,2), ...),結束 WSACleanup()。WSAGetLastError() 取錯誤碼。SO_REUSEADDR 避免 TIME_WAIT 干擾重啟。shutdown(s, SD_SEND) → 讀取剩餘資料直到 recv==0 → closesocket。WinSock API 以 SOCKET 描述符為核心,提供 socket/bind/connect/listen/accept/send/recv/closesocket 基礎原語。理解同步阻塞流程、緩衝區機制、TCP 位元組流本質、錯誤碼語意、非阻塞模型演進,是開發高效、穩健網路程式的基石。MFC CAsyncSocket/CSocket、Boost.Asio、libuv 等高階庫皆建立於此基礎之上。