設計報文廣播的最初目的是用於資源發送和減少數據交互量。但事實上,由於報文廣播時,同一網段內的所有主機,無論有沒有參與廣播應用,都必須完成對數據報的處理。被廣播的UDP報文會被接收主機的系統協議棧逐層處理,知道傳輸層將其交付監聽相應端口的應用進程或者丟棄。因此,頻繁的大數據量的報文廣播會嚴重影響網絡上其他主機的正常運行。而多播方式在具有廣播的優點同時,很好的解決了這個問題。
一個簡單的多播庫:
- //*************MCastLib.h***************//
- #ifndef _MCASTLIB_H_
- #define _MCASTLIB_H_
- #include<winsock2.h>
- #include<ws2tcpip.h>
- #ifdef __cplusplus
- extern "C" {
- #endif
- int mc_join(SOCKET s,struct in_addr *mcaddr,struct in_addr *local_if);
- int mc_setIF(SOCKET s,const DWORD local_out_if);
- int mc_getIF(SOCKET s,DWORD *local_out_if);
- int mc_setTTL(SOCKET s,const DWORD ttl);
- int mc_getTTL(SOCKET s,DWORD *ttl);
- int mc_setLoop(SOCKET s,const BOOL flag);
- int mc_getLoop(SOCKET s,BOOL *flag);
- int mc_leave(SOCKET s,struct in_addr *mcaddr,struct in_addr *local_if);
- #ifdef __cplusplus
- }
- #endif
- #endif
- //************************MCastLib.cpp*****************//
- #include "MCastLib.h"
- //本地接口local_if加入多播組mcaddr
- int mc_join(SOCKET s,struct in_addr *mcaddr,struct in_addr *local_if)
- {
- struct ip_mreq mreq;
- memcpy(&(mreq.imr_interface),local_if,sizeof(struct in_addr));
- memcpy(&(mreq.imr_multiaddr),mcaddr,sizeof(struct in_addr));
- return (setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char*)&mreq,sizeof(mreq)));
- }
- //爲多播報文設置外出接口
- int mc_setIF(SOCKET s,const DWORD local_out_if)
- {
- return (setsockopt(s,IPPROTO_IP,IP_MULTICAST_IF,(char*)&local_out_if,sizeof(local_out_if)));
- }
- //獲取多播報文的外出接口
- int mc_getIF(SOCKET s,DWORD *local_out_if)
- {
- int len = sizeof(DWORD);
- return (getsockopt(s,IPPROTO_IP,IP_MULTICAST_IF,(char*)local_out_if,&len));
- }
- //設置外出多播報文的ttl值,默認爲1
- int mc_setTTL(SOCKET s,const DWORD ttl)
- {
- return (setsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,(char*)&ttl,sizeof(ttl)));
- }
- //獲取外出多播報文的ttl值
- int mc_getTTL(SOCKET s,DWORD *ttl)
- {
- int len = sizeof(DWORD);
- return (getsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,(char*)ttl,&len));
- }
- //啓用或禁止多播報文迴環
- int mc_setLoop(SOCKET s,const BOOL flag)
- {
- return (setsockopt(s,IPPROTO_IP,IP_MULTICAST_LOOP,(char*)&flag,sizeof(flag)));
- }
- //獲取本地多播迴環狀態
- int mc_getLoop(SOCKET s,BOOL *flag)
- {
- int len = sizeof(BOOL);
- return (getsockopt(s,IPPROTO_IP,IP_MULTICAST_LOOP,(char*)flag,&len));
- }
- //本地接口local_if離開多播組mcaddr
- int mc_leave(SOCKET s,struct in_addr *mcaddr,struct in_addr *local_if)
- {
- struct ip_mreq mreq;
- memcpy(&(mreq.imr_interface),local_if,sizeof(struct in_addr));
- memcpy(&(mreq.imr_multiaddr),mcaddr,sizeof(struct in_addr));
- return (setsockopt(s,IPPROTO_IP,IP_DROP_MEMBERSHIP,(char*)&mreq,sizeof(mreq)));
- }
接收多播數據
- SOCKADDR_IN local;
- memset(&local,0,sizeof(SOCKADDR_IN));
- local.sin_family=AF_INET;
- local.sin_port=htons(5050);
- local.sin_addr.s_addr=inet_addr("202.119.9.199");
- bind(sock,(SOCKADDR*)&local,sizeof(SOCKADDR_IN));
- struct in_addr mcaddr;
- //202.119.9.199加入多播組226.1.2.3
- mcaddr.s_addr=inet_addr("226.1.2.3");
- mc_join(sock,&mcaddr,&(local.sin_addr));
- //接收數據
- char buf[65];
- while(TRUE)
- {
- memset(buf,0,65);
- recvfrom(sock,buf,65,0,NULL,NULL);
- }
上述代碼使套接字加入多播組226.1.2.3,並接受發往該組的數據。當調用mc_join函數時,內核會自動向該組發送一個“IGMP v2 Memebership Report”報文,該報文會被組內的所有主機及路由器接收。
在套接字關閉時,無論有沒有顯示的調用mc_leave函數,進程都會離開多播組。
發送多播數據
- SOCKET sock = socket(AF_INET,SOCK_DGRAM,0);
- //獲取默認的多播報文TTL值和迴環狀態
- DWORD ttl;
- BOOL loop;
- mc_getTTL(sock,&ttl);
- mc_getLoop(sock,&loop);
- printf("Multicast default: TTL=%d, LoopBack=%d\n",ttl,loop);
- //設置多播TTL值爲219
- ttl=219;
- mc_setTTL(sock,ttl);
- //想多播組發送數據
- SOCKADDR_IN to;
- memset(&to,0,sizeof(SOCKADDR_IN));
- to.sin_family=AF_INET;
- to.sin_port=htons(9999);
- to.sin_addr.s_addr=inet_addr("226.1.2.3");
- char *buf="hello!";
- int res = sendto(sock,buf,6,0,(SOCKADDR*)&to,sizeof(to));