组播原理简介:
当同一个网段内有多个IGMP路由器时,IGMP通过查询器选举机制从中选举出唯一的查询器。查询器周期性地发送普遍组查询消息进行成员关系查询,主机通过发送报告消息来响应查询。而作为组成员的路由器,其行为也与普通主机一样,响应其它路由器的查询。
当主机要加入组播组时,不必等待查询消息,而是主动发送报告消息;当主机要离开组播组时,也会主动发送离开组消息,查询器收到离开组消息后,会发送特定组查询消息来确定该组的所有组成员是否都已离开。
通过上述机制,在路由器里建立起一张表,其中记录了路由器各接口所对应子网上都有哪些组的成员。当路由器收到发往组G的组播数据后,只向那些有G的成员的接口转发该数据。至于组播数据在路由器之间如何转发则由组播路由协议决定,而不是IGMP的功能。
以下是由winsend发组播,客户端接受组播留,并写入一个文件的例子
代码语言:javascript复制/*******************************************************************************************
功能:组播客户端demo
时间:2014-03-23
说明:1.组播使用的igmp协议
2.组播源只有一份,由组播路由器转发。这样避免了单播造成组播源负载过重和网络拥塞拥塞的弊端
3.组播路由器只对加入组播组的成员转发数据,对网络上没有加入的组播源不发送数据,同样如果某个
子网离开组播组,组播路由器同样不会转发数据,这样就可以避免广播通讯所带来的广播风暴
4.每个设备只对它连接的设备单元可见。路由器(router)工作在三层(IP层),连接他的每个端口就是一个子网,
无论这个子网是一个主机还是由一个交换机连接的子网还是由集线器连接的共享网,对于路由器来说
只是一个子网
5.交换机(switch)连接的设备属于的一个子网,他们的网络地址对应路由器来说是一个。但交换机工作在mac层,
它能能够寻址和转发,因此在同一个交换机上各个端口是独立的。
6.集线器(HUB)连接主机是一个物理网,集线器没有寻址和转发功能,只是简单的分流,所以集线器连接网络是
一个共享网。比如一个集线器的入口是10M,如果连接在集线器上有10台主机,每个端口流量就1M。
集线器这个东西是历史产品,现在几乎已经很难可见。基本上switch已经渐渐充斥接入网和以太网。
3.自从在学校学习《计算机网络》《接入网技术》 《骨干网技术》,对路由器,交换机很含糊,特别是现在的交换机
可以做到三层,也就是交换机也具有路由功能,交换和路由的概念也越来越模糊,但笔者自认为:交换本质技术转接
交换的概念不是数据网独有,交换的概念最早来自有电话网,比如过去的电话网中的机械交换机机,数组程控交换机,当然
最早的交换工作就是电话接线员所做的工作。而路由是数据网(如IP网)才有的概念,路由的本质就是寻找路径,可能去目的地有
多条路径,路由算法可以找到最近或最合适的路径给传数据。传统电话网中采用的电路交换(时分复用,频分复用,
),不存在路由概念,而如今的IP采用的统计交换(统计时分复用),具有概率统计概念才有这种概念。
总之:路由采用寻找路径,骨干网上都是路由器,靠的IP地址来转发,而交换也是转发,靠的MAC地址,通常在子网或局域网中。
4.组播客户端传输层采用是UDP协议,跟一般的UDP操作差不多,无非多了一个加入组播和离开组播的动作
具体动作:1.创建socket;setsockopt加入组播
2.recvform接收组播数据
3.离开组播,释放socket
********************************************************************************************/
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
/*********************************************************'
函数名:leaveMulticastGroup
功能:退出组播
返回值:-1:退出失败;0:退出成功
********************************************************/
static int leaveMulticastGroup(char * multicastGroup,int socket_fd)
{
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(multicastGroup);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(socket_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&mreq, sizeof(mreq))<0)
{
printf("set socket opt is failden");
return -1;
}
printf("set socket opt is sucessedn");
return 0;
}
/****************************************************
函数名:joinMulticastGroup
功能:加入组播
返回值:-1:退出失败;0:退出成功
*****************************************************/
static int joinMulticastGroup(char * multicastGroup, int port, int * socketHandle)
{
struct ip_mreq stMreq;
struct sockaddr_in local_addr;
int socket_fd;
int reuse_flag = 1;
if (socketHandle == NULL) {
printf("socketHandle err.n");
return -1;
}
memset (&local_addr, 0,sizeof(struct sockaddr_in));
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons (port);
local_addr.sin_addr.s_addr = inet_addr(multicastGroup);
if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 )
{
printf("nsocket errorn");
return -1;;
}
/* Make socket reusable for PiP and record channels */
if(setsockopt( socket_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_flag,
sizeof(reuse_flag) ) < 0 )
{
printf("nSet REUSEADDR failed, PiP may not workn");
}
if (bind (socket_fd, (struct sockaddr *) &(local_addr),
sizeof (struct sockaddr)) == -1)
{
printf("nbind errorn");
return -1;
}
if (IN_MULTICAST(ntohl(local_addr.sin_addr.s_addr)))
{
stMreq.imr_multiaddr.s_addr = inet_addr(multicastGroup);
stMreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(socket_fd,
IPPROTO_IP,
IP_ADD_MEMBERSHIP,
(char *)&stMreq,
sizeof(stMreq)) < 0)
{
printf("nsetsockopt multicast add membership is failed!n");
}
}
printf("aaaaat");
*socketHandle = socket_fd;
return 0;
}
void main()
{
int socket,fd,len;
char buf[1316];
int port=8208;
struct sockaddr_in sersocket;
sersocket.sin_family= AF_INET;
sersocket.sin_port = htons(port);
sersocket.sin_addr.s_addr = inet_addr("192.168.1.108") ;
bzero(&(sersocket.sin_zero), 8);
char ipaddr[32] ={0};
int rc = -1;
strcpy(ipaddr,"225.1.1.1");
printf("0233n");
if(joinMulticastGroup( ipaddr,port,&socket))
{
printf("add MulticastGroup is failedn");
}
printf("0133n");
fd = fopen("rcv3.ts","w ");
while(1)
{
printf("0233n");
rc= recvfrom(socket,(void *)buf,1316,0,(struct sockaddr *) &sersocket, sizeof(struct sockaddr));
/*因为是组播,所以服务器地址信息可以不管,如果是点播的,一定要
/加上服务器socket地址信息*/
if (rc==-1)
{
printf("recv data is error!n");
break;
}
if(fwrite(buf,1316,1,fd) !=1316)
{
printf("fwirtie is failedn");
break;
}
}
joinMulticastGroup( ipaddr,port,&socket);
fclose(fd);
close(socket);
}