套接字是網路程式設計的核心抽象,掌握其建立、連線、資料傳輸、多工與關閉的完整生命週期,是開發高效、穩健網路應用的關鍵。本文以 TCP/IP 為基礎,系統講解 Client/Server 互動模型、七大基礎套接字 API、資料結構定義,並以三張時序圖呈現面向連線/無連線協定的呼叫流程與應用程式邏輯流向。
TCP/IP 網路中兩個進程的互動遵循 非對稱、非同步 的 Client/Server 模式:
socket() 建立套接字bind() 綁定本地 IP/埠listen() 監聽,進入被動等待狀態accept() 接受連線,回傳新套接字專責該客戶recv() 接收請求 → 處理 → send() 回應closesocket() 關閉客戶套接字,回 accept() 等待下一個socket() 建立套接字connect() 主動連線至伺服器 IP/埠send() 發送請求 → recv() 等待回應closesocket() 關閉連線SOCKET socket(int af, int type, int protocol);
af: AF_INET (IPv4), AF_INET6 (IPv6)
type: SOCK_STREAM (TCP), SOCK_DGRAM (UDP)
protocol: 0 (自動選擇), IPPROTO_TCP, IPPROTO_UDP
返回: 成功套接字描述符,失敗 INVALID_SOCKET
int bind(SOCKET s, const struct sockaddr* name, int namelen);
sockaddr_in 結構:
sin_family: AF_INET
sin_port: htons(埠號) // 網路位元組序
sin_addr.s_addr: htonl(INADDR_ANY) // 所有介面
sin_zero[8]: 保留歸零
返回: 0 成功,SOCKET_ERROR 失敗
int listen(SOCKET s, int backlog);
backlog: 等待佇列最大長度(建議 SOMAXCONN)
僅適用 SOCK_STREAM
// Client 主動連線
int connect(SOCKET s, const struct sockaddr* name, int namelen);
// Server 接受連線,回傳新套接字
SOCKET accept(SOCKET s, struct sockaddr* addr, int* addrlen);
addr: 回填客戶端 IP/埠
addrlen: 傳入/傳出緩衝區長度
int send(SOCKET s, const char* buf, int len, int flags);
int recv(SOCKET s, char* buf, int len, int flags);
flags: 0 一般, MSG_OOB (帶外數據), MSG_PEEK (窺視不移除)
返回: 實際傳送/接收位元組數,0 表對方關閉,SOCKET_ERROR 失敗
int select(int nfds, fd_set* readfds, fd_set* writefds,
fd_set* exceptfds, const struct timeval* timeout);
readfds: 可讀性檢測集合
writefds: 可寫性檢測集合
exceptfds: 例外狀況檢測集合
timeout: NULL 永久等待, {0,0} 非阻塞輪詢
返回: 就緒描述符總數,0 超時,SOCKET_ERROR 失敗
int closesocket(SOCKET s);
// 參考計數歸零時釋放核心資料結構
// TCP 發送 FIN,進入 TIME_WAIT
struct sockaddr_in {
short sin_family; // AF_INET
u_short sin_port; // 16-bit 埠號,網路序
struct in_addr sin_addr; // 32-bit IP,網路序
char sin_zero[8]; // 填零對齊
};
struct in_addr { union { u_long s_addr; } }; // IPv4 位址

圖:Server listen/accept ↔ Client connect → send/recv 交替 → 雙向 close

圖:Client sendto → Server recvfrom → Server sendto → Client recvfrom,無連線建立階段

圖:Server 主迴圈 accept → fork/thread 處理 → Client 連線收發迴圈
| 議題 | 關鍵點 |
|---|---|
| 位元組序 | 埠號/IP 必須 htons/htonl 轉網路序,接收後 ntohs/ntohl 轉主機序 |
| 部分傳送/接收 | send/recv 可能不足 len,必須迴圈處理剩餘資料 |
| 非阻塞模式 | ioctlsocket/FIONBIO 或 WSAEventSelect,配合 select 實現單執行緒多連線 |
| 優雅關閉 | shutdown(s, SD_SEND) 發送 FIN,持續 recv 至 0,再 closesocket |
| TIME_WAIT | 主動關閉方進入 2MSL 等待,SO_REUSEADDR 可讓新連線重用埠 |
| 錯誤處理 | WSAGetLastError() 取得錯誤碼:WSAEWOULDBLOCK、WSAECONNRESET、WSAETIMEDOUT 等 |