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

WinSock 程式設計學習筆記 (二):核心函式、I/O 模型與非同步通訊機制

     編輯:關於VC++

承接上篇基礎概念,本文深入探討 WinSock 核心 API 函式、位元組序轉換、IP 位址處理、Socket 狀態查詢,以及三種主流 I/O 模型:select、WSAAsyncSelect、WSAEventSelect 的原理與實作細節。

一、常用工具函式速查

1. 錯誤處理

int WSAGetLastError(void);
#define h_errno WSAGetLastError()

每次呼叫 WinSock 函式失敗後,立即呼叫此函式取得錯誤碼,詳細代碼請參考 Winsock2.h。

2. 位元組序轉換 (Host ↔ Network Order)

因不同 CPU 架構 (Little-Endian / Big-Endian) 儲存方式不同,網路輸統一使用 Big-Endian (Network Byte Order)。

函式用途範例
u_long htonl(u_long hostlong)32-bit 主機序 → 網路序htonl(80) = 1342177280
u_long ntohl(u_long netlong)32-bit 網路序 → 主機序ntohl(1342177280) = 80
u_short htons(u_short hostshort)16-bit 主機序 → 網路序htons(80) = 20480
u_short ntohs(u_short netshort)16-bit 網路序 → 主機序ntohs(20480) = 80

3. IP 位址字串與二進位互轉

// 點分十進位字串 → in_addr (u_long)
unsigned long inet_addr(const char FAR * cp);
// 範例:inet_addr("127.0.0.1") = 16777343
// 失敗回傳 INADDR_NONE

// in_addr → 點分十進位字串
char FAR * inet_ntoa(struct in_addr in);
// 注意:回傳指標指向靜態緩衝區,切勿修改或釋放

4. Socket 位址查詢

// 取得本地綁定位址
int getsockname(SOCKET s, struct sockaddr FAR *name, int FAR *namelen);
// 取得對端位址
int getpeername(SOCKET s, struct sockaddr FAR *name, int FAR *namelen);

5. 主機名稱與 IP 解析

// 取得本機主機名
int gethostname(char FAR *name, int namelen);

// 依主機名取得 hostent 結構 (含 IP 列表)
struct hostent FAR * gethostbyname(const char FAR *name);
// 範例:取得第一個 IP
hostent *host = gethostbyname("xiaojin");
if (host->h_addr_list[0]) {
  in_addr addr;
  memmove(&addr, host->h_addr_list[0], 4);
  char *ip = inet_ntoa(addr); // "127.0.0.1"
}

二、WinSock I/O 模型比較

1. 兩種基本 I/O 模式

  • 阻塞模式:預設。recv/send 完成前不返回,需搭配多執行緒處理多連線。
  • 非阻塞模式:函式立即返回,若資料未就緒回傳 WSAEWOULDBLOCK。邏輯複雜但擴展性強。

2. select 模型:同步多工

單一執行緒監控多個 Socket 狀態 (可讀/可寫/例外)。

int select(int nfds, fd_set FAR *readfds, fd_set FAR *writefds,
           fd_set FAR *exceptfds, const struct timeval FAR *timeout);

關鍵結構:

  • fd_set:含 fd_count 與 fd_array[FD_SETSIZE] (預設 64)。
  • timeval:tv_sec 秒 + tv_usec 微秒。設 {0,0} 為立即返回 (輪詢)。

常用巨集:FD_ZERO、FD_SET、FD_CLR、FD_ISSET。

fd_set fdread;
FD_ZERO(&fdread);
FD_SET(s, &fdread);
if (select(0, &fdread, NULL, NULL, NULL) > 0) {
  if (FD_ISSET(s, &fdread)) { /* 可讀 */ }
}

3. WSAAsyncSelect 模型:視窗訊息驅動

自動將 Socket 設為非阻塞,向指定視窗發送自訂訊息 (wMsg) 通知網路事件。

int WSAAsyncSelect(SOCKET s, HWND hWnd, u_int wMsg, long lEvent);
// lEvent 事件掩碼:FD_READ | FD_WRITE | FD_ACCEPT | FD_CONNECT | FD_CLOSE ...
// 取消通知:WSAAsyncSelect(s, hWnd, 0, 0);

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

4. WSAEventSelect 模型:事件物件驅動

與 WSAAsyncSelect 類似,但通知目標為 事件物件 (Event Object) 而非視窗。

  1. WSACreateEvent() 建立事件物件 (預設手動重置、非發送狀態)。
  2. WSAEventSelect(s, hEvent, lNetworkEvents) 關聯 Socket 與事件。
  3. I/O 處理後呼叫 WSAResetEvent(hEvent) 重置為非發送狀態。
  4. WSAWaitForMultipleEvents(...) 等待事件觸發 (支援多事件、超時、Alertable)。
  5. WSAEnumNetworkEvents(s, hEvent, &netEvents) 查詢具體事件與錯誤碼。
  6. WSACloseEvent(hEvent) 關閉事件物件。

三、ioctlsocket:Socket 控制介面

int ioctlsocket(SOCKET s, long cmd, u_long FAR *argp);

常用命令:

  • FIONREAD:取得接收緩衝區資料長度。
  • FIONBIO:啟用/停用非阻塞模式 (非 0 = 啟用)。
  • SIOCATMARK:檢查是否所有帶外資料已讀取。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved