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

Windows Socket 網絡編程(二):套接字編程核心原理與 API 全景解析

     編輯:關於VC++

Socket 是網絡通信的基石。本文系統梳理 **Client/Server 模型**、**核心 Socket API**、**連接建立/數據傳輸/關閉的時序流程**,並對比**面向連接 (TCP)** 與**無連接 (UDP)** 的調用差異,為 Windows 平台網絡編程提供清晰的 API 速查與原理參考。

一、 Client/Server 交互模型

TCP/IP 網絡中進程間通信遵循 **非對等、異步** 的 Client/Server 模式:

服務器端流程 (被動打開)

  1. socket() 創建套接字
  2. bind() 綁定本地 IP/端口 (Well-known Port)
  3. listen() 監聽,內核維護「已完成連接隊列」與「未完成連接隊列」
  4. accept() 從已完成隊列取出連接,**返回新套接字** 用於具體通信,原套接字繼續監聽
  5. 循環 recv()/send() 處理業務
  6. closesocket() 關閉連接套接字

客戶端流程 (主動打開)

  1. socket() 創建套接字
  2. connect() 主動發起三次握手連接服務器 IP/端口
  3. 循環 send()/recv() 交換數據
  4. closesocket() 關閉套接字

二、 核心 Socket API 詳細參考

1. socket() —— 創建套接字

SOCKET socket(int af, int type, int protocol);
// af: 地址族 AF_INET (IPv4), AF_INET6 (IPv6)
// type: SOCK_STREAM (TCP), SOCK_DGRAM (UDP), SOCK_RAW
// protocol: 0 (自動選擇), IPPROTO_TCP, IPPROTO_UDP
// 返回: INVALID_SOCKET 失敗, 否則套接字描述符

2. bind() —— 綁定本地地址

int bind(SOCKET s, const struct sockaddr* name, int namelen);
// sockaddr_in 結構 (IPv4):
struct sockaddr_in {
    short   sin_family;  // AF_INET
    u_short sin_port;    // 網絡字節序端口 (htons)
    struct in_addr sin_addr; // 網絡字節序 IP (inet_addr/inet_pton)
    char    sin_zero[8]; // 填充
};
// 返回: 0 成功, SOCKET_ERROR 失敗

3. connect() / accept() —— 建立連接


// 客戶端主動連接
int connect(SOCKET s, const struct sockaddr* name, int namelen);

// 服務器端接受連接 (阻塞直到有連接到達)
SOCKET accept(SOCKET s, struct sockaddr* addr, int* addrlen);
// 返回: 新套接字 (用於數據傳輸), INVALID_SOCKET 失敗
// addr/addrlen: 可選,返回客戶端地址信息

4. listen() —— 進入監聽狀態


int listen(SOCKET s, int backlog);
// backlog: 內核未完成/已完成連接隊列最大長度 (建議 SOMAXCONN)
// 僅適用 SOCK_STREAM

5. send() / recv() —— 數據傳輸


int send(SOCKET s, const char* buf, int len, int flags);
int recv(SOCKET s, char* buf, int len, int flags);
// flags: MSG_DONTROUTE, MSG_OOB (帶外數據), MSG_PEEK (窺探不移除)
// 返回: 發送/接收字節數, 0 (對方關閉), SOCKET_ERROR
// 注意: TCP 為流協議,需處理「粘包/半包」應用層邊界

6. select() —— 多路復用 (I/O 多路複用)


int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, const struct timeval* timeout);
// nfds: 最大套接字值 + 1 (Windows 忽略此參數)
// readfds: 關注可讀 (數據到達/連接關閉/accept 就緒)
// writefds: 關注可寫 (發送緩衝區可用/連接建立成功)
// exceptfds: 關注異常 (帶外數據等)
// timeout: NULL 永久阻塞, {0,0} 非阻塞輪詢, >0 最大等待
// 返回: 就緒套接字總數, 0 超時, SOCKET_ERROR
// 配合 FD_SET/FD_CLR/FD_ISSET/FD_ZERO 宏操作 fd_set

7. closesocket() —— 關閉套接字


int closesocket(SOCKET s);
// 引用計數歸零時發送 FIN (TCP) 或直接釋放 (UDP)
// 應確保發送緩衝區數據已發完 (或設置 SO_LINGER)

三、 典型時序圖解

1. TCP 面向連接套接字系統調用時序

TCP 套接字調用時序圖

關鍵點:listen 後內核為該套接字維護兩個隊列;accept 僅從「已完成連接隊列」取出,**不參與三次握手**;客戶端 connect 返回即代表握手完成。

2. UDP 無連接套接字調用時序

UDP 套接字調用時序圖

關鍵點:無 listen/accept;bind 通常由客戶端隱式綁定臨時端口;sendto/recvfrom 攜帶目標/源地址;connect 可用於 UDP 固定對端地址 (之後可用 send/recv)。

3. TCP 應用程序流程圖

TCP 應用程序流程圖

展示服務器多進程/多線程並發模型:accept 後 fork/CreateThread 處理子連接,父進程繼續 accept。

四、 進階實戰避坑指南

  • 字節序轉換:端口 htons()、IP htonl()/inet_pton(),接收端對應 ntohs()/ntohl()。
  • 非阻塞與異步:ioctlsocket(FIONBIO)、WSAAsyncSelect、WSAEventSelect、IOCP (Completion Port) 高性能模型。
  • TCP 粘包/半包:應用層定義協議幀 (長度前綴、分隔符、固定長度),recv 循環拼裝完整業務包。
  • TIME_WAIT 大量佔用:服務器主動關閉導致,設置 SO_REUSEADDR 允許端口快速重用。
  • 優雅關閉:shutdown(s, SD_SEND) 發送 FIN → 循環 recv 讀 0 → closesocket,確保數據雙向傳輸完畢。
  • 錯誤處理:WSAGetLastError() 獲取錯誤碼 (如 WSAEWOULDBLOCK, WSAECONNRESET)。

五、 總結

掌握 Socket 編程,核心在於理解 **「套接字是通信端點」**、**「TCP 是可靠字節流,UDP 是不可靠數據報」**、**「內核維護連接隊列與緩衝區」**。熟練使用 select/IOCP 實現高並發,正確處理字節序、粘包、優雅關閉,即可構建穩健的網絡應用。

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