现在的位置: 首页 > 综合 > 正文

组播

2013年08月26日 ⁄ 综合 ⁄ 共 7488字 ⁄ 字号 评论关闭

组播技术被认为是WWW技术推广之后出现的最激动人心的网络技术之一。1992年出现支持IP组播的Mbone(组播主干网)和Mbone桌面工具;1993-1996年IP Multicast成为业界关注的焦点,然而因发展条件不成熟使得IP组播只为业界所关注;进入1999年以来,IP组播具备了发展的三个关键条件:支持组播的路由协议;基于开放标准的可测试管理协议;因商业发展机遇而进入高速发展阶段。又一次掀起了组播实践的高潮,下面将有关组播应用作简单讨论:

5.1 组播主干网(Multicast Backbone:Mbone)
Mbone是一个由IETF开发的运行在Internet上的虚拟重叠网络。Mbone的初衷是创建一个半永久的IP组播测试床而不需要等到整个Internet都采用支持组播的路由器。
Mbone跨越几个洲,用户数大约在10000-30000之间。在IETF会议期间,大约有1000个不同的接收主机接入。它成为Internet上传送声音和视频信息的一个重要组成部分。
1992年,组播技术还处于实验阶段。当时提出以IP遂道(Tunneling)联结组播岛,组播岛是支持组播服务的区域,最小的组播岛是一个支持组播的LAN。
Mbone使用DVMRP协议,而DVMRP在UNIX下是由标准守护进程mrouted得以实现,所以许多用户使用UNIX主机接入Mbone。由于UNIX主机上的I/O处理能力、对IP遂道的处理能力、网络接口数量等方面都不及商用路由器,这都无形制约了Mbone的发展。
Mbone自从出现就不断发展。今天,从基于mrouted的UNIX主机到商用路由器的迁移已超过了50%;Mbone也采用剪枝、封装等技术。新的域间组播路由协议和转发算法、流量控制与管理、可靠组播也将对Mbone产生影响。

5.2 组播应用程序接口与编程
RFC1112推荐了一些支持组播的应用程序接口:
●加入一个组播组;
●离开一个组播组;
●为调整范围对一个组播数据的IP TTL值进行设定;
●为组播传输和接收设定本地的接口;
●禁止输出的组播数据回送。
现在,许多TCP/IP实现都支持RFC1112所提到的要求,下面简要介绍UNIX(Berkeley Socket)和Windows(Winsock) API。
5.2.1 Berkeley Socket组播API
所有Berkeley Socket API都采用setsockopt()的“套接字选项”功能来设置(对于某些选项,getsockopt()功能可用来获得当前的设置)。表3描述了Berkeley BSD的set sockopt()/getsockopt()组播命令。
表3 BSD setsockopt()/getsockopt()组播命令的说明
 
 

setsockopt()/getsockopt()组播命令

命令说明

IP_MULTICAST_TTL

设置输出组播数据的TTL

IP_ADD_MEMBERSHIP

在指定接口上加入组播组

IP_DROP_MEMBERSHIP

退出组播组(在IGMPv2中实现)

IP_MULTICAST_IF

获取默认接口或设置接口

IP_MULTICAST_LOOP

禁止组播数据回送

 
 
对于套接字编程,首先要使用函数socket()建立一个数据包套接字,然后用bind()函数将套接字与一个地址和端口号连接起来。
为了发送一个组播数据包,需要在sendto()调用中指定一个组播地址作为目的地址(所有IP地址都使用网络字节顺序)。
为了接收一个组播数据包,需要在recvfrom()调用中指定所要接收的组播地址。
IP_MULTICAST_TTL允许将随后的组播数据的TTL设定成从0到255之间的任何值,例如:
u_char ttl;
setsockopt(sock,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl));
关于TTL的讨论见上文。
通过IP_MULTICAST_IF,系统管理员可在安装的时候为组播创建默认的接口(为从一个给定的网络接口并发传送,一个网络接口会忽略这个默认值)。例如:
struct in_addr addr;
setsockopt(sock,IPPROTO_IP,IP_MULTICAST_IF,&addr,sizeof(addr));
在这里,addr是希望输出接口的本地IP地址,可使用一个INADDR_ANY地址来回送到默认的接口。
当组播组中的一台主机发送组播数据到输出接口时,默认的IP层将为本地回送数据的拷贝。
IP_MULTICAST_LOOP网络参数控制IP层是否回送所送的数据。例如:
u_char loop;
setsockopt(sock,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop));
将loop设置为0则禁止回送,设置为1则允许回送。
为了能够接收IP组播数据,主机必须加入某个或多个组播组,程序通过使用IP_ADD_MEMBERSHIP网络接口参数向主机提出加入组播组的申请。例如:
struct ip_mreq
{struct in_addr imn_multiaddr; /* multicast group to join */
struct in_addr imr_interface; /* interface to join on */
}mreq;
setsockopt(sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
一个组的成员是与一个单一的网络接口相联系;主机可在不止一个网络接口上加入相同的组。若选择默认组播接口,要将imr_interface设置为INADDR_ANY;若选择主机其中一个本地地址,要将imr_interface设置为特定的组播接口。
若撤消一个成员资格,使用IP_DROP_MEMBERSHIP
struct ip_mreq mreq;
setsockopt(sock,IPPROTP_IP,IP_DROP_MEMBERSHIP,&mreq,sizeof(sreq));
其中mreq包含了在IP_ADD_MEMBERSHIP命令中相同的值。
5.2.2 Windows Socket组播API
基于Winsock1.1的组播编程与Berkeley Socket类似,这里不再赘述。Winsock2是Winsock1.1的扩展,除兼容Berkeley Sockets组播API外,它还定义了一套支持IP组播的协议独立API,如表4所示:
表4 WinSock 2的协议独立组播API说明
 

WSAEnum Protocol()

获得协议信息结构(WASPROTOCOL_INFO)

WSASocket()

设置组播类型

WSAJoinLeaf

加入组播组并指定角色(发送者/接收者)

WSAIoctl(…SIO_MULTICAST_SCOPE…)

设置IP TTL

WSAIoctl(…SIO_MULTICAST_LOOPBACK…)

禁止组播数据回送

 
在Winsock2中,定义了“数据平面”(Data Plane)和“控制平面”(Control Plane)的概念,其中,数据平面决定在不同的网络成员之间数据如何传送;控制平面定义网络成员的组织方式; 这两方面的特征既可以是“有根的”(Rooted),也可以是“无根的”(Nonrooted)。在“有根的”控制平面内,存在一个特殊的组播组成员,称作C_root(根节点),其余的组成员称作C_leaf(叶节点)。对“无根的”控制平面而言,只存在叶节点。
在“有根的”平面中,根节点负责组播的建立,以及同任意数量叶节点的连接。叶节点可申请加入一个特定的组播组。数据传送只能在根节点和叶节点之间进行,根节点将数据组播到每个叶节点。
在“无根的”平面中,只存在叶节点,它们可以任意加入一个组播组。从叶节点发送的数据会组播到每一个叶节点。
由于篇幅所限,有关Winsock API的进一步讨论,请参阅参考文献<3>、<5>和MSDN

 

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFLEN 255
/*********************************************************************
*filename: mcastclient.c
*purpose: 演示组播编程的基本步骤,其实这就是一个基本的UDP客户端程序
*tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-25 13:10:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
struct sockaddr_in peeraddr, myaddr;

int sockfd;
char recmsg[BUFLEN + 1];
unsigned int socklen;

/* 创建 socket 用于UDP通讯 */
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
printf("socket creating error/n");
exit(1);
}
socklen = sizeof(struct sockaddr_in);

/* 设置对方的端口和IP信息 */
memset(&peeraddr, 0, socklen);
peeraddr.sin_family = AF_INET;
if (argv[2])
peeraddr.sin_port = htons(atoi(argv[2]));
else
peeraddr.sin_port = htons(7838);
if (argv[1]) {
/* 注意这里设置的对方地址是指组播地址,而不是对方的实际IP地址 */
if (inet_pton(AF_INET, argv[1], &peeraddr.sin_addr) <= 0) {
printf("wrong group address!/n");
exit(0);
}
} else {
printf("no group address!/n");
exit(0);
}

/* 设置自己的端口和IP信息 */
memset(&myaddr, 0, socklen);
myaddr.sin_family = AF_INET;
if (argv[4])
myaddr.sin_port = htons(atoi(argv[4]));
else
myaddr.sin_port = htons(23456);

if (argv[3]) {
if (inet_pton(AF_INET, argv[3], &myaddr.sin_addr) <= 0) {
printf("self ip address error!/n");
exit(0);
}
} else
myaddr.sin_addr.s_addr = INADDR_ANY;

/* 绑定自己的端口和IP信息到socket上 */
if (bind
(sockfd, (struct sockaddr *) &myaddr,
sizeof(struct sockaddr_in)) == -1) {
printf("Bind error/n");
exit(0);
}

/* 循环接受用户输入的消息发送组播消息 */
for (;;) {
/* 接受用户输入 */
bzero(recmsg, BUFLEN + 1);
if (fgets(recmsg, BUFLEN, stdin) == (char *) EOF)
exit(0);
/* 发送消息 */
if (sendto
(sockfd, recmsg, strlen(recmsg), 0,
(struct sockaddr *) &peeraddr,
sizeof(struct sockaddr_in)) < 0) {
printf("sendto error!/n");
exit(3);
}
printf("'%s' send ok/n", recmsg);
}
}

 

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>

#define BUFLEN 255
/*********************************************************************
*filename: mcastserver.c
*purpose: 演示组播编程的基本步骤,组播服务器端,关键在于加入组
*tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-25 13:20:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
struct sockaddr_in peeraddr;
struct in_addr ia;
int sockfd;
char recmsg[BUFLEN + 1];
unsigne int socklen, n;
struct hostent *group;
struct ip_mreq mreq;

/* 创建 socket 用于UDP通讯 */
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
printf("socket creating err in udptalk/n");
exit(1);
}

/* 设置要加入组播的地址 */
bzero(&mreq, sizeof(struct ip_mreq));
if (argv[1]) {
if ((group = gethostbyname(argv[1])) == (struct hostent *) 0) {
perror("gethostbyname");
exit(errno);
}
} else {
printf
("you should give me a group address, 224.0.0.0-239.255.255.255/n");
exit(errno);
}

bcopy((void *) group->h_addr, (void *) &ia, group->h_length);
/* 设置组地址 */
bcopy(&ia, &mreq.imr_multiaddr.s_addr, sizeof(struct in_addr));

/* 设置发送组播消息的源主机的地址信息 */
mreq.imr_interface.s_addr = htonl(INADDR_ANY);

/* 把本机加入组播地址,即本机网卡作为组播成员,只有加入组才能收到组播消息 */
if (setsockopt
(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
sizeof(struct ip_mreq)) == -1) {
perror("setsockopt");
exit(-1);
}

socklen = sizeof(struct sockaddr_in);
memset(&peeraddr, 0, socklen);
peeraddr.sin_family = AF_INET;
if (argv[2])
peeraddr.sin_port = htons(atoi(argv[2]));
else
peeraddr.sin_port = htons(7838);
if (argv[1]) {
if (inet_pton(AF_INET, argv[1], &peeraddr.sin_addr) <= 0) {
printf("Wrong dest IP address!/n");
exit(0);
}
} else {
printf("no group address given, 224.0.0.0-239.255.255.255/n");
exit(errno);
}

/* 绑定自己的端口和IP信息到socket上 */
if (bind
(sockfd, (struct sockaddr *) &peeraddr,
sizeof(struct sockaddr_in)) == -1) {
printf("Bind error/n");
exit(0);
}

/* 循环接收网络上来的组播消息 */
for (;;) {
bzero(recmsg, BUFLEN + 1);
n = recvfrom(sockfd, recmsg, BUFLEN, 0,
(struct sockaddr *) &peeraddr, &socklen);
if (n < 0) {
printf("recvfrom err in udptalk!/n");
exit(4);
} else {
/* 成功接收到数据报 */
recmsg[n] = 0;
printf("peer:%s", recmsg);
}
}
}

 

编译程序用下列命令:
gcc -Wall mcastclient.c -o mcastclient
gcc -Wall mcastserver.c -o mcastserver
运行程序用如下命令:
./mcastserver 230.1.1.1 7838
客户端程序运行命令为:
./mcastclient 230.1.1.1 7838 192.168.100.1 12345

抱歉!评论已关闭.