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