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

UDP 網路程式設計實作:封包收發與 WinSock 初始化完整範例

     編輯:關於C++

UDP 通訊核心概念

UDP(User Datagram Protocol)是無連線、不可靠的傳輸協定,適合即時性要求高、容忍少量封包遺失的場合。本文示範直接使用 WinSock API 實作 UDP 收發,而非使用封裝元件,以獲得更精細控制。

關鍵變數與參數說明

  • Destino:目標 IP 位址(無符號長整數)。0 表示接受任意來源封包,接收後自動更新為發送者位址;指定位址則只接受該來源資料。
  • gvEncerrar:全域終止旗標,設為真時中斷處理迴圈。
  • gvSocket:全域 Socket 控制代碼。
  • gvPortUdp:UDP 通訊埠號。
  • 逾時機制:Inicio + 12 秒(12 秒逾時)。

核心函式實作

1. ReadBufferUdp:接收封包至緩衝區

int ReadBufferUdp(unsigned long *Destino, void *T, int Size) {
    char Buffer[128];
    SOCKADDR_IN SockAddr;
    int LenSockAddr = sizeof(SOCKADDR_IN);
    fd_set FdRead;
    struct timeval t_val;
    int Ret;
    time_t Inicio = time(NULL);
    Application->ProcessMessages();
    if (gvEncerrar) return false;

    FD_ZERO(&FdRead);
    FD_SET(gvSocket, &FdRead);
    t_val.tv_sec = 0;
    t_val.tv_usec = 0;

    // 等待資料或逾時
    while ((Ret = select(0, &FdRead, NULL, NULL, &t_val)) != 1 && 
           (Inicio + 12) > time(NULL) && !gvEncerrar) {
        FD_ZERO(&FdRead);
        FD_SET(gvSocket, &FdRead);
        t_val.tv_sec = 0;
        t_val.tv_usec = 0;
        Application->ProcessMessages();
    }
    if (Ret != 1) return false;

    // 接收資料
    if (recvfrom(gvSocket, Buffer, Size, 0, (LPSOCKADDR)&SockAddr, &LenSockAddr) != Size)
        return false;

    // 位址驗證與更新
    if (*Destino == 0) {
        *Destino = SockAddr.sin_addr.s_addr;  // 記錄首次發送者位址
    } else if (*Destino != SockAddr.sin_addr.s_addr) {
        return false;  // 非預期來源
    }

    memcpy(T, Buffer, Size);
    return true;
}

2. WriteBufferUdp:發送封包

int WriteBufferUdp(unsigned long Destino, void *T, int Size) {
    SOCKADDR_IN SockAddr;
    int Sent;
    Application->ProcessMessages();
    SockAddr.sin_family = AF_INET;
    SockAddr.sin_port = gvPortUdp;
    SockAddr.sin_addr.s_addr = Destino;
    Sent = sendto(gvSocket, (char*)T, Size, 0, (LPSOCKADDR)&SockAddr, sizeof(SockAddr));
    return (Sent == Size);
}

3. InicializaTCPIP:WinSock 初始化與 Socket 建立

void InicializaTCPIP() {
    WORD wVersionRequested = MAKEWORD(1, 1);
    WSADATA wsaData;
    IN_ADDR In;
    PSERVENT PServent;
    SOCKADDR_IN SockAddrIn;

    // 載入 WinSock DLL
    if (WSAStartup(wVersionRequested, &wsaData)) {
        ShowMessage("TCP/IP 初始化失敗");
        Application->Terminate();
        return;
    }

    // 從服務檔取得埠號
    if ((PServent = getservbyname("your_service_name", "udp")) == NULL) {
        ShowMessage("無法取得服務埠號");
        Application->Terminate();
        return;
    }
    gvPortUdp = PServent->s_port;
    // 記錄埠號日誌...

    // 建立 UDP Socket
    if ((gvSocket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
        ShowMessage("Socket 建立失敗");
        Application->Terminate();
        return;
    }
    // 記錄成功日誌...

    // 綁定 Socket
    SockAddrIn.sin_family = AF_INET;
    SockAddrIn.sin_port = gvPortUdp;
    SockAddrIn.sin_addr.s_addr = INADDR_ANY;  // NULL 等同 INADDR_ANY
    if (bind(gvSocket, (LPSOCKADDR)&SockAddrIn, sizeof(SockAddrIn)) == SOCKET_ERROR) {
        ShowMessage("Socket 綁定失敗");
        Application->Terminate();
        return;
    }
    // 記錄綁定成功日誌...
}

實作要點總結

  1. 非阻塞輪詢:使用 select() 搭配零逾時結構實現非阻塞檢查,配合 Application->ProcessMessages() 保持介面回應。
  2. 位址動態學習:Destino=0 模式自動學習首個發送者位址,後續僅接受該位址資料,適合簡單點對點通訊。
  3. 服務名稱解析埠號:透過 getservbyname() 從系統服務檔(%windir%\system32\drivers\etc\services)讀取埠號,提升可維護性。
  4. 錯誤處理機制:每個關鍵步驟皆檢查回傳值,失敗時顯示訊息並終止應用程式,確保資源正確釋放。

此架構可作為 UDP 通訊模組的基礎,擴充時可加入封包序號、重傳機制或加密模組,構建可靠的 UDP 應用協定。

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