本文系統整理 Windows Socket (WinSock 2.0) 程式設計的核心基礎知識,涵蓋 Socket 類型、必要開發檔案、關鍵資料結構、協議常數、Socket 選項,以及從初始化到關閉的完整通訊流程與程式碼範例。
Socket 相當於網路通訊兩端的「插座」,定義為 typedef unsigned int SOCKET;。五種類型中最常用三種:
| 類型 | 常數 | 特性 |
|---|---|---|
| 流式套接字 (Stream) | SOCK_STREAM (1) | TCP、連線導向、可靠、有序、無邊界、適合大量資料 |
| 資料報套接字 (Datagram) | SOCK_DGRAM (2) | UDP、無連線、不可靠、有邊界、效率高、支援廣播 |
| 原始套接字 (Raw) | SOCK_RAW (3) | 保留完整 IP 標頭,適用封包分析、防火牆開發 |
Winsock2.hWS2_32.LIBWS2_32.DLLtypedef unsigned char u_char;
typedef unsigned short u_short;
typedef unsigned int u_int;
typedef unsigned long u_long;
舊版 in_addr (聯合體):含 s_b1..s_b4、s_w1..s_w2、S_addr (常用)。
新版 in_addr:直接為 u_long S_addr。
範例:INADDR_LOOPBACK (127.0.0.1) = 0x7F000001。
sockaddr:通用結構,sa_family (通常 AF_INET) + sa_data[14]。
sockaddr_in:專用於 TCP/IP (Internet 域),實務上一律使用此結構:
struct sockaddr_in {
short sin_family; // AF_INET
u_short sin_port; // 埠號 (網路序,用 htons)
struct in_addr sin_addr; // IP 位址 (u_long)
char sin_zero[8]; // 填充,對齊用
};
常用宏:INADDR_ANY (0) 表示綁定所有本機介面。
struct hostent {
char FAR * h_name; // 官方主機名
char FAR * FAR * h_aliases; // 別名列表
short h_addrtype; // 位址類型 (AF_INET)
short h_length; // 位址長度 (4)
char FAR * FAR * h_addr_list; // IP 位址列表 (多網卡)
#define h_addr h_addr_list[0]
};
IPPROTO_IP 0
IPPROTO_ICMP 1
IPPROTO_TCP 6
IPPROTO_UDP 17
IPPROTO_RAW 255
透過 getsockopt/setsockopt 讀寫,層級 level 通常為 IPPROTO_IP 或 SOL_SOCKET。常用選項:
| 選項 | 值 | 說明 |
|---|---|---|
| SO_REUSEADDR | 0x0004 | 允許本地位址重用 |
| SO_KEEPALIVE | 0x0008 | 保持連線 (心跳) |
| SO_BROADCAST | 0x0020 | 允許廣播 |
| SO_LINGER | 0x0080 | 關閉時延遲傳送未送資料 |
| SO_SNDBUF / SO_RCVBUF | 0x1001/0x1002 | 發送/接收緩衝區大小 |
| SO_SNDTIMEO / SO_RCVTIMEO | 0x1005/0x1006 | 發送/接收逾時 (毫秒) |
WORD wVersion = MAKEWORD(2,0);
WSADATA wsData;
int nResult = WSAStartup(wVersion, &wsData);
if (nResult != 0) { /* 錯誤處理 */ }
// 成功後 wsData.wVersion=2, wHighVersion=514 (2.2)
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sock == INVALID_SOCKET) { /* 錯誤處理 */ }
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(0); // 0 = 系統自動指派
addr.sin_addr.s_addr = inet_addr("192.168.1.100");
int nResult = bind(sock, (sockaddr*)&addr, sizeof(sockaddr));
listen(sock, 5); // 最大等待佇列 5
sockaddr_in clientAddr;
SOCKET clientSock = accept(sock, (sockaddr*)&clientAddr, &addrLen);
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(8080);
serverAddr.sin_addr.s_addr = inet_addr("192.168.1.200");
connect(sock, (sockaddr*)&serverAddr, sizeof(sockaddr));
// 發送
char buf[] = "Hello";
send(sock, buf, strlen(buf), 0);
// 接收
char recvBuf[1024];
int len = recv(sock, recvBuf, 1024, 0);
Flags 參數:0 (預設)、MSG_OOB (帶外/緊急)、MSG_PEEK (窺視不移除)、MSG_DONTROUTE (不經路由)。
shutdown(sock, SD_BOTH); // SD_RECEIVE(0)/SD_SEND(1)/SD_BOTH(2)
closesocket(sock);
WSACleanup(); // 進程結束前呼叫一次
掌握上述結構與流程,即可撰寫基本的 TCP/UDP 用戶端/伺服器程式。進階主題 (非阻塞 I/O、IOCP、SSL/TLS、IPv6) 將於後續筆記說明。