Winsock(Windows Sockets)是 Windows 平台上網路程式設計的標準 API,支援多種協定(以 TCP/IP 最常用)。本文詳細說明基於 Winsock API 的 Client/Server 架構實作,涵蓋伺服器/客戶端 Socket 生命週期、非同步通知機制、資料收發函數、完整程式碼範例。
專案需包含三個關鍵檔案:
winsock.h(或現代 winsock2.h) - 標頭檔wsock32.lib(或 ws2_32.lib) - 連結庫,需加入專案設定winsock.dll - 系統目錄下的動態連結庫WSADATA wsaData;
WSAStartup(MAKEWORD(1,1), &wsaData); // 要求 1.1 版本
SOCKET sListen = socket(AF_INET, SOCK_STREAM, 0);
// AF_INET: IPv4
// SOCK_STREAM: TCP 串流
// 0: 自動選擇協定
失敗回傳 INVALID_SOCKET,可用 WSAGetLastError() 取得錯誤碼。
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(10205); // 埠號,網路位元組序
addr.sin_addr.s_addr = INADDR_ANY; // 監聽所有介面
bind(sListen, (sockaddr*)&addr, sizeof(addr));
listen(sListen, 5); // 最大連接佇列長度 5
WSAAsyncSelect避免阻塞式 accept(),使用視窗訊息驅動:
WSAAsyncSelect(sListen, hWnd, WM_USER_SOCKET, FD_ACCEPT | FD_CLOSE);
hWnd:接收訊息的視窗WM_USER_SOCKET:自訂訊息lParam 低字節:事件類型(FD_ACCEPT、FD_READ、FD_WRITE、FD_CLOSE 等)accept()case FD_ACCEPT:
SOCKET sClient = accept(sListen, NULL, NULL);
// 新 Socket 處理此客戶端,原監聽 Socket 繼續監聽
WSAAsyncSelect(sClient, hWnd, WM_USER_SOCKET, FD_READ | FD_CLOSE);
break;
TCP (串流):
send(sClient, buffer, len, 0);
recv(sClient, buffer, len, 0);
UDP (資料報):
sendto(s, buf, len, 0, (sockaddr*)&to, sizeof(to));
recvfrom(s, buf, len, 0, (sockaddr*)&from, &fromlen);
TCP 保證可靠、有序、不重複;UDP 輕量但無保證。
closesocket(sClient);
closesocket(sListen);
WSACleanup();
// 1. 初始化
WSAStartup(MAKEWORD(1,1), &wsaData);
// 2. 建立 Socket
SOCKET sClient = socket(AF_INET, SOCK_STREAM, 0);
// 3. 連線伺服器
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(10205);
inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
connect(sClient, (sockaddr*)&serverAddr, sizeof(serverAddr));
// 4. 收發資料
send(sClient, "Hello Server", 12, 0);
recv(sClient, buffer, sizeof(buffer), 0);
// 5. 關閉
closesocket(sClient);
WSACleanup();
WSAAsyncSelect 事件驅動架構完整範例本範例封裝 CNetworking 類別,支援回呼函數處理連線、接收、關閉事件,無需視窗也能運作。
void ReceiveCallback(DWORD ptr) {
CConnection* c = (CConnection*)ptr;
char buff[1024];
c->Receive(buff, 1024);
MessageBox(hWnd, buff, "Received", MB_OK);
}
void AcceptCallback(DWORD ptr) {
CNetworking* net = (CNetworking*)ptr;
CConnection* c = net->GetAccepted();
if (c) {
c->SetReceiveFunc(ReceiveCallback);
c->SetCloseFunc(CloseCallback);
// 取得客戶端資訊...
}
}
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDM_LISTEN:
Networking.SetAcceptFunc(AcceptCallback);
Networking.Listen(CONNECT_PORT);
break;
case IDM_CONNECT:
DialogBox(hInst, "CONNECTIP", hWnd, ConnectToIP);
break;
case IDM_SENDMSG:
DialogBox(hInst, "SENDMSG", hWnd, SendString);
break;
}
break;
| 旗標 | 說明 |
|---|---|
ios::app | 附加寫入 |
ios::ate | 開啟後定位檔尾 |
ios::trunc | 截斷檔案(預設) |
ios::nocreate | 不存在不建立 |
ios::noreplace | 存在則失敗 |
Winsock API 雖屬底層介面,但掌握 socket/bind/listen/accept/connect/send/recv 核心流程與 WSAAsyncSelect 非同步機制,即可建立高效穩定的網路應用。封裝類別可進一步簡化開發,提升程式碼複用性。