客戶端與伺服端核心差異
| 步驟 | 伺服端 | 客戶端 |
| 1. 綁定 IP/Port | ✅ bind() | ❌ 不需要 |
| 2. 監聽連線 | ✅ listen() | ❌ 不需要 |
| 3. 接受連線 | ✅ accept() | ❌ 不需要 |
| 4. 主動連線 | ❌ | ✅ connect() |
Visual Studio 6.0 多專案工作區建立
- File → New → Projects 選擇 Win32 Console Application,命名為
Server
- 同方案下 Insert → Project 新增
Client 專案
- 兩專案皆需連結
Ws2_32.lib:
專案 → Settings → Link → Object/Library modules 加入 Ws2_32.lib
客戶端完整原始碼 (Console)
#include <Winsock2.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
int main()
{
// 1. 協商版本
WSADATA wsaData;
WORD wVersion = MAKEWORD(2, 2); // 建議使用 2.2
int err = WSAStartup(wVersion, &wsaData);
if (err != 0) {
printf("WSAStartup 失敗: %d\n", err);
return 1;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
WSACleanup();
printf("Winsock 2.2 不可用\n");
return 1;
}
// 2. 建立 Socket
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockClient == INVALID_SOCKET) {
printf("socket() 失敗: %d\n", WSAGetLastError());
WSACleanup();
return 1;
}
// 3. 目標伺服器地址
sockaddr_in serverAddr = {};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(27015);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 本機測試
// 4. 連線伺服器
if (connect(sockClient, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
printf("連線失敗: %d\n", WSAGetLastError());
closesocket(sockClient);
WSACleanup();
return 1;
}
printf("已連線至伺服器\n");
// 5. 接收歡迎訊息
char recvBuf[128] = {};
int nRecv = recv(sockClient, recvBuf, sizeof(recvBuf) - 1, 0);
if (nRecv > 0) {
recvBuf[nRecv] = '\0';
printf("收到: %s\n", recvBuf);
}
// 6. 發送訊息
const char* sendMsg = "this is client message";
send(sockClient, sendMsg, (int)strlen(sendMsg) + 1, 0); // 含結束符
printf("已發送: %s\n", sendMsg);
// 7. 清理
closesocket(sockClient);
WSACleanup();
return 0;
}
關鍵點補充
- 版本協商:建議
MAKEWORD(2,2),支援 IPv6、重疊 I/O 等新特性
- 錯誤處理:所有 Socket API 失敗時需呼叫
WSAGetLastError() 取得錯誤碼
- 緩衝區大小:
recv 回傳實際收到位元組數,**必須手動加結束符** 才能用 printf("%s")
- 發送長度:
send 第三參數為位元組數,字串常加 +1 發送 \0 方便接收端判斷
- 阻塞模式:預設為阻塞,
recv 無資料時會等待;生產環境常改用非阻塞 + select/WSAAsyncSelect/IOCP
下一步擴展方向
- 多執行緒:每連線一執行緒,或執行緒池
- GUI 介面:MFC
CAsyncSocket 或 CSocket 封裝
- 協定設計:定義訊息頭 (長度、類型)、心跳、重連機制
- 安全性:整合 OpenSSL 實現 TLS 加密傳輸