IP 多播允許單一發送者將封包傳遞給多個接收者,適用於區域網路即時廣播、股票行情推播、多點會議等場景。Windows 透過 setsockopt 設定 IP_ADD_MEMBERSHIP 等選項即可實作。本文提供基於 Winsock 2 的極簡收發範例。
224.0.0.0 ~ 239.255.255.255;區域網路保留 224.0.0.0/24 (TTL=1 不跨路由)。| 選項 | 層級 | 用途 |
|---|---|---|
IP_ADD_MEMBERSHIP | IPPROTO_IP | 加入多播群組 |
IP_DROP_MEMBERSHIP | IPPROTO_IP | 離開群組 |
IP_MULTICAST_TTL | IPPROTO_IP | 封包生存躍點數 (1=本地段) |
IP_MULTICAST_LOOP | IPPROTO_IP | 是否回送本機 (0=關閉) |
SO_REUSEADDR | SOL_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;
}
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 或應用層可靠性協定,滿足金融、工控等嚴格場景。