设计报文广播的最初目的是用于资源发送和减少数据交互量。但事实上,由于报文广播时,同一网段内的所有主机,无论有没有参与广播应用,都必须完成对数据报的处理。被广播的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));
转载于:https://blog.51cto.com/zcwtop/806208