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

Winsock UDP 網路程式設計完整指南(C++ 實作)

     編輯:關於C++

在 Windows 平台上進行網路程式開發時,Winsock(Windows Sockets)是核心 API。相較於面向連線的 TCP 協定,UDP(User Datagram Protocol)是一種無連線、不可靠的傳輸協定,但因其低延遲、低開銷的特性,廣泛應用於即時通訊、影音串流、線上遊戲等場景。本文將詳細解析基於 UDP 的 Winsock 程式設計要點,並提供完整的 Server/Client 範例程式碼。

UDP 與 TCP 程式設計的核心差異

雖然初始化與清理流程相同,但 UDP 省去了連線建立的步驟:

  • Server 端:不需要呼叫 listen() 和 accept(),直接在 bind() 後進入資料收發迴圈。
  • Client 端:不需要呼叫 connect(),直接指定目標位址發送資料。

資料收發函數改為 sendto() 和 recvfrom(),兩者都需要顯式指定目標/來源的 Socket 位址結構。

關鍵函數原型解析

int sendto(SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen);
int recvfrom(SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen);

  • flags:通常設為 0。
  • to / from:目標或來源的位址結構指標(sockaddr_in)。
  • tolen / fromlen:位址結構的大小。

完整程式碼範例

UDP Server 端

/* UDPServer.cpp */
#include <winsock2.h>
#include <stdio.h>
#include <windows.h>
#pragma comment(lib, "WS2_32.lib")

int main() {
    // 1. 初始化 Winsock 庫
    WSADATA wsaData;
    WORD wVersion = MAKEWORD(2, 2);
    if (WSAStartup(wVersion, &wsaData) != 0) {
        printf("Winsock 初始化失敗\n");
        return 1;
    }

    // 2. 建立 UDP Socket (SOCK_DGRAM)
    SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s == INVALID_SOCKET) {
        printf("Socket 建立失敗: %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    // 3. 設定本地位址並綁定
    sockaddr_in serverAddr{};
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(7500); // 使用較高埠號避免衝突
    serverAddr.sin_addr.S_un.S_addr = INADDR_ANY;

    if (bind(s, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        printf("Bind 失敗: %d\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    printf("UDP Server 已啟動,監聽埠 7500...\n");

    // 4. 收發資料迴圈
    sockaddr_in clientAddr{};
    int clientAddrLen = sizeof(clientAddr);
    char recvBuf[1024] = {0};
    const char* sendMsg = "歡迎連線到 UDP Server!\r\n";

    while (true) {
        int recvLen = recvfrom(s, recvBuf, sizeof(recvBuf) - 1, 0,
                               (sockaddr*)&clientAddr, &clientAddrLen);
        if (recvLen > 0) {
            recvBuf[recvLen] = '\0';
            printf("收到來自 %s:%d 的訊息: %s",
                   inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port), recvBuf);

            // 回覆 Client
            sendto(s, sendMsg, (int)strlen(sendMsg), 0,
                   (sockaddr*)&clientAddr, clientAddrLen);
        }
    }

    // 5. 清理資源 (實際上無限迴圈不會執行到這裡,需加入退出機制)
    closesocket(s);
    WSACleanup();
    return 0;
}

UDP Client 端

/* UDPClient.cpp */
#include <winsock2.h>
#include <stdio.h>
#include <windows.h>
#pragma comment(lib, "WS2_32.lib")

int main() {
    WSADATA wsaData;
    WORD wVersion = MAKEWORD(2, 2);
    if (WSAStartup(wVersion, &wsaData) != 0) return 1;

    SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s == INVALID_SOCKET) {
        WSACleanup();
        return 1;
    }

    // 設定 Server 目標位址
    sockaddr_in serverAddr{};
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(7500);
    serverAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 本機測試

    const char* sendMsg = "Server 你好!\r\n";
    char recvBuf[1024] = {0};
    sockaddr_in fromAddr{};
    int fromLen = sizeof(fromAddr);

    printf("UDP Client 已啟動,送資料給 127.0.0.1:7500\n");

    // 發送資料
    if (sendto(s, sendMsg, (int)strlen(sendMsg), 0,
               (sockaddr*)&serverAddr, sizeof(serverAddr)) != SOCKET_ERROR) {
        printf("資料已發送,等待回應...\n");
        // 接收回應
        int recvLen = recvfrom(s, recvBuf, sizeof(recvBuf) - 1, 0,
                               (sockaddr*)&fromAddr, &fromLen);
        if (recvLen > 0) {
            recvBuf[recvLen] = '\0';
            printf("Server 回應: %s", recvBuf);
        }
    }

    closesocket(s);
    WSACleanup();
    return 0;
}

關鍵實作細節補充

  1. 錯誤處理:所有 Socket 函數回傳值皆應檢查,失敗時透過 WSAGetLastError() 取得錯誤碼。
  2. 位址轉換:埠號需用 htons() 轉網路位元組序,IP 字串轉整數用 inet_addr(),整數轉字串用 inet_ntoa()。
  3. 緩衝區大小:UDP 封包有大小限制(理論上限 65,507 Bytes),實際建議控制在 MTU (通常 1500 Bytes) 內避免分片。
  4. 非阻塞/多工:實際專案常搭配 select()、WSAAsyncSelect() 或 IOCP 實作非阻塞多工通訊。

總結

UDP 程式設計核心在於「無連線」與「訊息邊界保留」。掌握 sendto/recvfrom 與位址結構的操作,即可快速建立高效能的網路應用。建議讀者實際編譯執行上述程式碼,觀察封包在 Wireshark 中的傳輸細節,加深理解。

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