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

Winsock API 網路程式設計實戰:TCP/IP Client/Server 完整實作

     編輯:關於VC++

前言

Winsock(Windows Sockets)是 Windows 平台上網路程式設計的標準 API,支援多種協定(以 TCP/IP 最常用)。本文詳細說明基於 Winsock API 的 Client/Server 架構實作,涵蓋伺服器/客戶端 Socket 生命週期、非同步通知機制、資料收發函數、完整程式碼範例。

一、開發環境設定

專案需包含三個關鍵檔案:

  • winsock.h(或現代 winsock2.h) - 標頭檔
  • wsock32.lib(或 ws2_32.lib) - 連結庫,需加入專案設定
  • winsock.dll - 系統目錄下的動態連結庫

二、伺服器端 Socket 操作流程

2.1 初始化 Winsock

WSADATA wsaData;
WSAStartup(MAKEWORD(1,1), &wsaData); // 要求 1.1 版本

2.2 建立監聽 Socket

SOCKET sListen = socket(AF_INET, SOCK_STREAM, 0);
// AF_INET: IPv4
// SOCK_STREAM: TCP 串流
// 0: 自動選擇協定

失敗回傳 INVALID_SOCKET,可用 WSAGetLastError() 取得錯誤碼。

2.3 綁定 IP 與埠

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));

2.4 進入監聽狀態

listen(sListen, 5); // 最大連接佇列長度 5

2.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 等)

2.6 接受連線 accept()

case FD_ACCEPT:
    SOCKET sClient = accept(sListen, NULL, NULL);
    // 新 Socket 處理此客戶端,原監聽 Socket 繼續監聽
    WSAAsyncSelect(sClient, hWnd, WM_USER_SOCKET, FD_READ | FD_CLOSE);
    break;

2.7 資料收發

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 輕量但無保證。

2.8 關閉與清理

closesocket(sClient);
closesocket(sListen);
WSACleanup();

三、客戶端 Socket 操作流程

// 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 非同步機制,即可建立高效穩定的網路應用。封裝類別可進一步簡化開發,提升程式碼複用性。

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