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

Winsock 實作區域網路多播 (Multicast) 收發範例

     編輯:VC++

前言

IP 多播允許單一發送者將封包傳遞給多個接收者,適用於區域網路即時廣播、股票行情推播、多點會議等場景。Windows 透過 setsockopt 設定 IP_ADD_MEMBERSHIP 等選項即可實作。本文提供基於 Winsock 2 的極簡收發範例。

多播基礎知識

  • 多播位址範圍: 224.0.0.0 ~ 239.255.255.255;區域網路保留 224.0.0.0/24 (TTL=1 不跨路由)。
  • 通訊協定: 必須使用 UDP (SOCK_DGRAM);TCP 不支援多播。
  • 關鍵 Socket 選項:
選項層級用途
IP_ADD_MEMBERSHIPIPPROTO_IP加入多播群組
IP_DROP_MEMBERSHIPIPPROTO_IP離開群組
IP_MULTICAST_TTLIPPROTO_IP封包生存躍點數 (1=本地段)
IP_MULTICAST_LOOPIPPROTO_IP是否回送本機 (0=關閉)
SO_REUSEADDRSOL_SOCKET允許同埠多實例 (多程序接收)

完整收發範例 (主控台應用程式)

#include 
#include 
#include 
#include 
#include 

#pragma comment(lib, "ws2_32.lib")

const char* MCAST_IP   = "224.0.0.99";  // 區域網路多播位址
const int   MCAST_PORT = 2002;
const int   BUF_SIZE   = 1024;

void SenderThread(void* param)
{
    SOCKET s = (SOCKET)param;
    sockaddr_in dest = {0};
    dest.sin_family = AF_INET;
    dest.sin_port = htons(MCAST_PORT);
    dest.sin_addr.s_addr = inet_addr(MCAST_IP);

    char line[BUF_SIZE];
    while (std::cin.getline(line, BUF_SIZE))
    {
        if (_strnicmp(line, "quit", 4) == 0) break;
        sendto(s, line, (int)strlen(line), 0, (sockaddr*)&dest, sizeof(dest));
    }
    std::cout << "Sender exit.\n";
}

void ReceiverThread(void* param)
{
    SOCKET s = (SOCKET)param;
    sockaddr_in from; int fromLen = sizeof(from);
    char buf[BUF_SIZE];

    while (true)
    {
        int n = recvfrom(s, buf, BUF_SIZE, 0, (sockaddr*)&from, &fromLen);
        if (n == SOCKET_ERROR)
        {
            int err = WSAGetLastError();
            if (err == WSAEINTR) break;  // 主執行緒關閉 socket 觸發
            std::cerr << "recvfrom error: " << err << "\n";
            break;
        }
        if (n > 0)
        {
            buf[n] = '\0';
            std::cout << "Recv: " << buf << "\n";
        }
    }
    std::cout << "Receiver exit.\n";
}

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

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

    // 允許同機多實例接收
    int opt = 1;
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));

    // 設定 TTL=10 (可跨越少數路由器)
    int ttl = 10;
    setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(ttl));

    // 關閉本機回送 (不接收自己發送的封包)
    int loop = 0;
    setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char*)&loop, sizeof(loop));

    // 綁定本地埠
    sockaddr_in local = {0};
    local.sin_family = AF_INET;
    local.sin_port = htons(MCAST_PORT);
    local.sin_addr.s_addr = INADDR_ANY;
    bind(s, (sockaddr*)&local, sizeof(local));

    // 加入多播群組
    ip_mreq mreq = {0};
    mreq.imr_multiaddr.s_addr = inet_addr(MCAST_IP);
    mreq.imr_interface.s_addr = INADDR_ANY;
    setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq));

    // 啟動收發執行緒
    HANDLE hThreads[2];
    hThreads[0] = (HANDLE)_beginthread(SenderThread, 0, (void*)s);
    hThreads[1] = (HANDLE)_beginthread(ReceiverThread, 0, (void*)s);

    // 等待發送執行緒結束 (輸入 quit)
    WaitForSingleObject(hThreads[0], INFINITE);

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

跨網段/路由器注意事項

  1. TTL 值: 必須 >1 才能跨越路由器;每經過一層路由 TTL -1。
  2. IGMP Snooping / Proxy: 交換器/路由器需支援 IGMP 管理多播轉發,否則封包會泛洪全網段或被丟棄。
  3. 防火牆: 需開放 UDP 埠 (範例 2002) 及 IGMP 協定 (IP 協定號 2)。
  4. 多網卡介面: imr_interface 指定發送/接收網卡 IP,而非 INADDR_ANY,確保封包從正確介面出入。

生產環境強化建議

  • 使用 WSASocket + WSAEventSelect/IOCP 替代 _beginthread,支援高併發。
  • 封裝 sendto/recvfrom 為非同步操作,避免阻塞執行緒。
  • 加入序列號/時間戳/檢查和,偵測封包遺失、重複、亂序。
  • 實作心跳機制,偵測接收者離線並自動重發關鍵資料。
  • 考慮使用 PGM (Pragmatic General Multicast) 協定 (Windows 支援 WSAPROTOCOL_INFO),獲得可靠多播傳輸。

結語

Winsock 多播核心只需 socket → setsockopt(IP_ADD_MEMBERSHIP) → bind → sendto/recvfrom 五步驟。理解 TTL、Loopback、介面綁定三大關鍵參數,即可在區域網路快速部署高效廣播應用。進階需求可引入 PGM 或應用層可靠性協定,滿足金融、工控等嚴格場景。

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