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

Visual C++ 實作區域網路 IP 多播通訊完整指南

     編輯:關於VC++

前言:為什麼需要多播?

在區域網路中,管理員常需將資訊發送給特定用戶群組。若採用一對一單播 (Unicast) 方式,不僅繁瑣,還易造成漏發、錯發。多播 技術應運而生,它是基於 IP 層的群組通訊技術,能高效實現「一對多」傳輸,並有效減輕網路負載、避免資源浪費。

核心概念辨析

單播 vs 多播 vs 廣播

  • 單播:一對一點對點通訊,傳統 TCP/IP 基礎模式。
  • 廣播:一對全體,封包發送至子網內所有主機,每台機器都必須處理,資源浪費嚴重。
  • 多播:一對多 (有興趣者),資料僅傳遞給加入特定多播群組的進程,兼具效率與精準性。

IP 多播技術基礎

1. 協定支援

Win32 平台上,僅 IP 與 ATM 協定支援多播。本文聚焦於普遍使用的 TCP/IP 協定族。支援平台涵蓋 Windows CE 2.1+、Windows 95/98/NT 4.0/2000/XP 等。範例基於 Windows XP Professional 開發。

2. 多播位址 (D 類位址)

IP 使用 D 類位址 (224.0.0.0 ~ 239.255.255.255) 識別多播群組,28 位元群組 ID 允許超過 2 億個同時群組。傳送至 D 類位址的封包會盡力送達所有群組成員,但不保證可靠送達。

位址說明
224.0.0.0保留,不可使用
224.0.0.1子網內所有系統 (主機)
224.0.0.2子網內所有路由器
224.0.0.5所有 OSPF 路由器
224.0.0.9RIP v2 群組
224.0.1.1網路時間協定 (NTP)
224.0.1.24WINS 伺服器群組

永久位址 由 IANA 保留分配;暫時位址 需動態建立,進程可隨時加入/離開,最後一個進程離開時群組消失。

3. 多播路由器與 IGMP

多播路由器 (亦可兼任普通路由器) 每分鐘向 224.0.0.1 發送 IGMP 查詢,主機回報感興趣的 D 類位址。路由器間透過修改過的距離向量協定交換資訊,為每個群組構建覆蓋所有成員的生成樹,並透過剪枝優化刪除無關路徑。

4. WinSock API 支援

  • WinSock 1:使用 setsockopt() 設定 IP_ADD_MEMBERSHIP 選項加入群組。
  • WinSock 2 (Win98+ 預裝):引入 WSAJoinLeaf() 專用函數,協定無關,功能更強。本範例基於 WinSock 2.0 實作。

程式設計步驟與架構

本範例包含兩個獨立控制台程式:Sender (發送端) 與 Receiver (接收端)。

Receiver 實作流程

  1. 建立 SOCK_DGRAM 型別 Socket。
  2. 綁定本地連接埠 (如 5150) 接收資料。
  3. 加入多播群組 (呼叫 WSAJoinLeaf,旗標 JL_BOTH 或 JL_RECEIVER_ONLY)。
  4. 循環呼叫 recvfrom() 接收資料,收到 "QUIT" 退出。

Sender 實作流程

  1. 建立 SOCK_DGRAM 型別 Socket。
  2. 加入多播群組 (同樣呼叫 WSAJoinLeaf)。
  3. 從主控台讀取輸入,呼叫 sendto() 發送至多播位址,輸入 "QUIT" 結束。

完整原始碼實作

Receiver.c

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>

#define MCASTADDR "233.0.0.1"  // 多播群組位址
#define MCASTPORT 5150         // 本地連接埠
#define BUFSIZE 1024

int main(int argc, char **argv) {
    WSADATA wsd;
    struct sockaddr_in local, remote, from;
    SOCKET sock, sockM;
    char recvbuf[BUFSIZE];
    int len = sizeof(struct sockaddr_in), ret;

    // 初始化 WinSock 2.2
    if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
        printf("WSAStartup() failed\n"); return -1;
    }

    // 建立支援多點的 Overlapped Socket
    if ((sock = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0,
        WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF | WSA_FLAG_OVERLAPPED))
        == INVALID_SOCKET) {
        printf("socket failed: %d\n", WSAGetLastError());
        WSACleanup(); return -1;
    }

    // 綁定本地連接埠
    local.sin_family = AF_INET;
    local.sin_port = htons(MCASTPORT);
    local.sin_addr.s_addr = INADDR_ANY;
    if (bind(sock, (struct sockaddr*)&local, sizeof(local)) == SOCKET_ERROR) {
        printf("bind failed: %d\n", WSAGetLastError());
        closesocket(sock); WSACleanup(); return -1;
    }

    // 加入多播群組 (WinSock 2 方式)
    remote.sin_family = AF_INET;
    remote.sin_port = htons(MCASTPORT);
    remote.sin_addr.s_addr = inet_addr(MCASTADDR);

    if ((sockM = WSAJoinLeaf(sock, (SOCKADDR*)&remote, sizeof(remote),
        NULL, NULL, NULL, NULL, JL_BOTH)) == INVALID_SOCKET) {
        printf("WSAJoinLeaf() failed: %d\n", WSAGetLastError());
        closesocket(sock); WSACleanup(); return -1;
    }

    // 接收循環
    while (1) {
        if ((ret = recvfrom(sock, recvbuf, BUFSIZE, 0,
            (struct sockaddr*)&from, &len)) == SOCKET_ERROR) {
            printf("recvfrom failed: %d\n", WSAGetLastError());
            break;
        }
        if (strcmp(recvbuf, "QUIT") == 0) break;
        recvbuf[ret] = '\0';
        printf("RECV: '%s' FROM <%s>\n", recvbuf, inet_ntoa(from.sin_addr));
    }

    closesocket(sockM); closesocket(sock); WSACleanup();
    return 0;
}

Sender.c

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>

#define MCASTADDR "233.0.0.1"
#define MCASTPORT 5150
#define BUFSIZE 1024

int main(int argc, char **argv) {
    WSADATA wsd;
    struct sockaddr_in remote;
    SOCKET sock, sockM;
    char sendbuf[BUFSIZE];
    int len = sizeof(struct sockaddr_in);

    if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
        printf("WSAStartup() failed\n"); return -1;
    }

    if ((sock = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0,
        WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF | WSA_FLAG_OVERLAPPED))
        == INVALID_SOCKET) {
        printf("socket failed: %d\n", WSAGetLastError());
        WSACleanup(); return -1;
    }

    // 加入多播群組
    remote.sin_family = AF_INET;
    remote.sin_port = htons(MCASTPORT);
    remote.sin_addr.s_addr = inet_addr(MCASTADDR);

    if ((sockM = WSAJoinLeaf(sock, (SOCKADDR*)&remote, sizeof(remote),
        NULL, NULL, NULL, NULL, JL_BOTH)) == INVALID_SOCKET) {
        printf("WSAJoinLeaf() failed: %d\n", WSAGetLastError());
        closesocket(sock); WSACleanup(); return -1;
    }

    // 發送循環
    while (1) {
        printf("SEND: ");
        scanf("%s", sendbuf);
        if (sendto(sockM, sendbuf, strlen(sendbuf), 0,
            (struct sockaddr*)&remote, sizeof(remote)) == SOCKET_ERROR) {
            printf("sendto failed: %d\n", WSAGetLastError());
            break;
        }
        if (strcmp(sendbuf, "QUIT") == 0) break;
        Sleep(500);
    }

    closesocket(sockM); closesocket(sock); WSACleanup();
    return 0;
}

測試部署步驟

  1. 將 Sender.exe 複製至發送端電腦。
  2. 將 Receiver.exe 複製至多台接收端電腦。
  3. 各自執行對應程式。
  4. 在 Sender 主控台輸入文字,所有 Receiver 端即可即時收到相同資料。

小結

IP 多播技術透過 D 類位址與 IGMP 協定,高效實現了區域網路內的群組通訊。WinSock 2 的 WSAJoinLeaf 介面簡化了群組管理,配合 UDP 無連線特性,極適合即時廣播、視訊會議、股票行情推送等場景。掌握此技術,能為網路應用開發提供強有力的架構選項。

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