在區域網路中,管理員常需將資訊發送給特定用戶群組。若採用一對一單播 (Unicast) 方式,不僅繁瑣,還易造成漏發、錯發。多播 技術應運而生,它是基於 IP 層的群組通訊技術,能高效實現「一對多」傳輸,並有效減輕網路負載、避免資源浪費。
Win32 平台上,僅 IP 與 ATM 協定支援多播。本文聚焦於普遍使用的 TCP/IP 協定族。支援平台涵蓋 Windows CE 2.1+、Windows 95/98/NT 4.0/2000/XP 等。範例基於 Windows XP Professional 開發。
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.9 | RIP v2 群組 |
| 224.0.1.1 | 網路時間協定 (NTP) |
| 224.0.1.24 | WINS 伺服器群組 |
永久位址 由 IANA 保留分配;暫時位址 需動態建立,進程可隨時加入/離開,最後一個進程離開時群組消失。
多播路由器 (亦可兼任普通路由器) 每分鐘向 224.0.0.1 發送 IGMP 查詢,主機回報感興趣的 D 類位址。路由器間透過修改過的距離向量協定交換資訊,為每個群組構建覆蓋所有成員的生成樹,並透過剪枝優化刪除無關路徑。
setsockopt() 設定 IP_ADD_MEMBERSHIP 選項加入群組。WSAJoinLeaf() 專用函數,協定無關,功能更強。本範例基於 WinSock 2.0 實作。本範例包含兩個獨立控制台程式:Sender (發送端) 與 Receiver (接收端)。
SOCK_DGRAM 型別 Socket。WSAJoinLeaf,旗標 JL_BOTH 或 JL_RECEIVER_ONLY)。recvfrom() 接收資料,收到 "QUIT" 退出。SOCK_DGRAM 型別 Socket。WSAJoinLeaf)。sendto() 發送至多播位址,輸入 "QUIT" 結束。#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;
}
#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;
}
Sender.exe 複製至發送端電腦。Receiver.exe 複製至多台接收端電腦。IP 多播技術透過 D 類位址與 IGMP 協定,高效實現了區域網路內的群組通訊。WinSock 2 的 WSAJoinLeaf 介面簡化了群組管理,配合 UDP 無連線特性,極適合即時廣播、視訊會議、股票行情推送等場景。掌握此技術,能為網路應用開發提供強有力的架構選項。