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

Windows Socket API 核心函數深度解析:WSAStartup 到 Send/Recv 完整流程與最佳實踐

     編輯:關於C語言

Socket 程式設計啟動與清理:WSAStartup / WSACleanup


// 請求版本 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:socket()


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();
    // 處理錯誤
}
參數常用值說明
afAF_INET (IPv4), AF_INET6 (IPv6)位址家族
typeSOCK_STREAM (TCP), SOCK_DGRAM (UDP), SOCK_RAWSocket 類型
protocolIPPROTO_TCP, IPPROTO_UDP, 0 (自動)協議,通常 0 即可

Socket 描述符本質:進程私有的索引值,指向核心 Socket 物件。核心物件有引用計數,closesocket 遞減計數,歸零時釋放。

關閉 Socket:closesocket()


int ret = closesocket(s);
if (ret == SOCKET_ERROR) { /* WSAGetLastError() */ }

行為:

  • 引用計數 = 1 → 移除描述符表項 + 釋放核心物件 (發送 FIN/關閉連線)。
  • 引用計數 > 1 (經 WSADuplicateSocket 或 fork 等) → 僅移除表項,遞減計數。

TCP 連線建立:connect / bind / listen / accept

Client 端


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... */ }

Server 端


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 繼續監聽

資料收發:send / recv (同步阻塞模式)

send()


int send(SOCKET s, const char* buf, int len, int flags);
// flags: 0 通常; MSG_DONTROUTE, MSG_OOB (帶外資料)
  • 阻塞直到資料被複製到發送緩衝區 (不保證對端已收到)。
  • 返回實際發送位元組數 (可能 < len),需迴圈發送剩餘資料。
  • 連線斷開返回 SOCKET_ERROR,WSAGetLastError() == WSAECONNRESET。

recv()


int recv(SOCKET s, char* buf, int len, int flags);
// flags: 0 通常; MSG_PEEK (窺視不移除), MSG_OOB
  • 阻塞直到至少 1 位元組可讀或連線關閉。
  • 返回值:>0 實際讀取位元組;=0 對端優雅關閉 (收到 FIN);<0 錯誤。
  • TCP 是位元組流,無訊息邊界,應用層必須自行定義協議 (長度前綴、分隔符、固定長度)。

UDP 無連線收發:sendto / recvfrom


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 返回發送端地址,可直接回覆。
  • 不保證送達、順序、不重複。

非阻塞與異步 I/O 模型

模型API適用場景複雜度
阻塞預設簡單 Client、獨立執行緒 Server低
非阻塞ioctlsocket(FIONBIO)單執行緒輪詢、select 配合中
selectselect()跨平台、連線數 < FD_SETSIZE (1024)中
WSAAsyncSelectWSAAsyncSelect(hWnd, msg)MFC/Win32 GUI 應用,訊息驅動低 (MFC 封裝)
WSAEventSelectWSAEventSelect + WSAWaitForMultipleEventsConsole/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、埠號錯誤

最佳實踐清單

  1. 啟動時 WSAStartup(MAKEWORD(2,2), ...),結束 WSACleanup()。
  2. 所有 Socket 操作檢查返回值,WSAGetLastError() 取錯誤碼。
  3. TCP 發送/接收實作迴圈處理部分發送/接收。
  4. 應用層協議設計明確訊息邊界 (長度頭、分隔符、Protobuf 等)。
  5. Server 端設定 SO_REUSEADDR 避免 TIME_WAIT 干擾重啟。
  6. 生產環境必須使用非阻塞/IOCP,避免單執行緒阻塞導致服務凍結。
  7. 關閉連線流程:shutdown(s, SD_SEND) → 讀取剩餘資料直到 recv==0 → closesocket。
  8. 記錄關鍵錯誤碼與狀態,便於事後診斷。

總結

WinSock API 以 SOCKET 描述符為核心,提供 socket/bind/connect/listen/accept/send/recv/closesocket 基礎原語。理解同步阻塞流程、緩衝區機制、TCP 位元組流本質、錯誤碼語意、非阻塞模型演進,是開發高效、穩健網路程式的基石。MFC CAsyncSocket/CSocket、Boost.Asio、libuv 等高階庫皆建立於此基礎之上。

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