http://blog.csdn.net/langeldep/article/details/6167137
http://liuqz926.blog.163.com/blog/static/13448936220091121104233491/
http://blog.csdn.net/zlzlei/article/details/7692987
http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/example/cpp03/multicast/receiver.cpp
http://blog.csdn.net/laohuang1122/article/details/7102827
http://www.cnblogs.com/my_life/articles/6077569.html 看这个吧,这个比较好
首先选一个多播地址和端口,数据接收方选择一个网卡监听这个多播地址和端口,数据发送方向这个多播地址和端口发送数据,这样监听了多播地址与端口的接收方就都可以收到发送方的数据了,没有监听的自然也就收不到了!!!
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <string.h> #include "my_inet.h" #include <arpa/inet.h> #define MAXBUF 256 #define PUERTO 5000 #define GRUPO "224.0.1.1" int main(void) { int fd, n, r; struct sockaddr_in srv, cli; struct ip_mreq mreq; char buf[MAXBUF]; memset( &srv, 0, sizeof(struct sockaddr_in) ); memset( &cli, 0, sizeof(struct sockaddr_in) ); memset( &mreq, 0, sizeof(struct ip_mreq) ); srv.sin_family = MY_AF_INET; srv.sin_port = htons(PUERTO); if( inet_aton(GRUPO, &srv.sin_addr ) < 0 ) { perror("inet_aton"); return -1; } if( (fd = socket( MY_AF_INET, SOCK_DGRAM, MY_IPPROTO_UDP) ) < 0 ){ perror("socket"); return -1; } if( bind(fd, (struct sockaddr *)&srv, sizeof(srv)) < 0 ){ /* 绑定一个多播地址与端口,只有该地址发来的数据才会被接收 */ perror("bind"); return -1; } if (inet_aton(GRUPO, &mreq.imr_multiaddr) < 0) { perror("inet_aton"); return -1; } inet_aton( "172.16.48.2", &(mreq.imr_interface) ); if( setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(mreq)) < 0 ){ /* 将一个网卡(172.16.48.2)绑定到socket的多播地址(224.0.1.1)上 */ perror("setsockopt"); return -1; } n = sizeof(cli); while(1){ if( (r = recvfrom(fd, buf, MAXBUF, 0, (struct sockaddr *)&cli, (socklen_t*)&n)) < 0 ){ /* 正常的接收即可 */ perror("recvfrom"); }else{ buf[r] = 0; fprintf(stdout, "Mensaje desde %s: %s", inet_ntoa(cli.sin_addr), buf); } } }
这是一个非常简单的组播客户端,bind()操作指定只能从组播组224.0.1.1的5000端口读数据,并显示在终端上,下面我们通过分析该程序来了解内核的工作过程。
前面我们讲过,bind操作首先检查用户指定的端口是否可用,然后为socket的一些成员设置正确的值,并添加到哈希表myudp_hash中。然后,协议栈每次收到UDP数据,就会检查该数据报的源和目的地址,还有源和目的端口,在myudp_hash中找到匹配的socket,把该数据报放入该socket的接收队列,以备用户读取。在这个程序中,bind操作把socket绑定到地址224.0.0.1:5000上, 该操作产生的直接结果就是,对于socket本身,下列值受影响:
struct inet_sock{
.rcv_saddr = 224.0.0.1; //对于组播而言,只接收该地址发来的组播数据;对于单播而言,只从该地址所代表的网卡接收数据
.saddr = 0.0.0.0; //发出去的数据包由该地址所代表的网卡发出去。为0,系统(在进行路由选择时)任意选一个地址填充发出去的数据包
.sport = 5000; //通过该端口发送接收数据
.daddr = 0.0.0.0; //目的地址不确定,connect()或sendto()时会被填充
.dport = 0;
}
这五个数据表示,该套接字在发送数据包时,本地使用端口5000,本地可以使用任意一个网络设备接口,发往的目的地址不指定。在接收数据时,只接收发往IP地址224.0.0.1的端口为5000的数据。
程序中,紧接着bind有一个setsockopt操作,它的作用是将socket加入一个组播组(用来指示使用哪个网卡接收哪个组播组的数据),因为socket要接收组播地址224.0.0.1的数据,它就必须加入该组播组。
对于UDP组播而言,bind(本地IP,port)不再适用,不是绑定本地local地址的效果,而是一种过滤规则,只接收来自该地址和端口的组播数据。
bind之后,必须要有setsockopt该动作,因为bind()操作对于组播,它会把saddr置为0,此时系统就无从下手,不知道选择哪个网卡来接收组播数据。
同样,对于组播数据的发送方,如果也调用了bind()操作,会导致saddr = 0; 这会使用系统默认的网卡发送组播数据。如果需要指定某个网卡来发送数据,需要setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr))来设置saddr.
结构体struct ip_mreq mreq是该操作的参数,下面是其定义:
struct ip_mreq
{
struct in_addr imr_multiaddr; // 组播组的IP地址。
struct in_addr imr_interface; // 本地某一网络设备接口的IP地址。
};
一台主机上可能有多块网卡,接入多个不同的子网,imr_interface参数就是指定一个特定的设备接口,告诉协议栈只想在这个设备所在的子网中加入某个组播组。有了这两个参数,协议栈就能知道:在哪个网络设备接口上加入哪个组播组。为了简单起见,我们的程序中直接写明了IP地址:在172.16.48.2所在的设备接口上加入组播组224.0.1.1。
这个操作是在网络层上的一个选项,所以级别是SOL_IP,IP_ADD_MEMBERSHIP选项把用户传入的参数拷贝成了struct ip_mreqn结构体:
struct ip_mreqn
{
struct in_addr imr_multiaddr;
struct in_addr imr_address;
int imr_ifindex;
};
多了一个输入接口的索引,暂时被拷贝成零。
该操作最终引发内核函数myip_mc_join_group执行加入组播组的操作。首先检查imr_multiaddr是否为合法的组播地址,然后根据imr_interface的值找到对应的struct in_device结构。接下来就要为socket加入到组播组了,在inet_sock的结构体中有一个成员mc_list,它是一个结构体struct ip_mc_socklist的链表,每一个节点代表socket当前正加入的一个组播组,该链表是有上限限制的,缺省值为IP_MAX_MEMBERSHIPS(20),也就是说一个socket最多允许同时加入20个组播组。下面是struct ip_mc_socklist的定义:
struct ip_mc_socklist
{
struct ip_mc_socklist *next;
struct ip_mreqn multi;
unsigned int sfmode; /* MCAST_{INCLUDE,EXCLUDE} */
struct ip_sf_socklist *sflist;
};
struct ip_sf_socklist
{
unsigned int sl_max;
unsigned int sl_count;
__u32 sl_addr[0];
};
除了multi成员,它还有一个源过滤机制。如果我们新添加的struct ip_mreqn已经存在于这个链表中(表示socket早就加入这个组播组了),那么不做任何事情,否则,创建一个新的struct ip_mc_socklist:
struct ip_mc_socklist
{
.next = inet->mc_list; //新节点放到链表头。
.multi = 传入的参数; //这是关键的组信息。
.sfmode = MCAST_EXCLUDE; //过滤掉sflist中的所有源。
.sflist = NULL; //没有源需要过滤。
};
最后,调用myip_mc_inc_group函数在struct in_device和struct net_device的mc_list链表中都添上相应的组播组节点,关于这部分的细节可以在前一篇文章《初识组播2》中找到。不再重复。
==============================================================
http://blog.chinaunix.net/uid-31125416-id-5705162.html
单播(Unicast)
组播(Multicast)、多播
广 播(Broadcast)
2.2 实现IP组播的前提条件
实现IP组播传输,则组播源和接收者以及两者之间的下层网络都必须支持组播。这包括以下几方面:
●主机的TCP/IP实现支持发送和接收IP组播;
●主机的网络接口支持组播;
●有一套用于加入、离开、查询的组管理协议,即IGMP(v1,v2);
●有一套IP地址分配策略,并能将第三层IP组播地址映射到第二层MAC地址;
●支持IP组播的应用软件;
●所有介于组播源和接收者之间的路由器、集线器、交换机、TCP/IP栈、防火墙均需支持组播;
2.3 组播地址分配与MAC地址
在组播通信中,我们需要两种地址:一个IP组播地址和一个Ethernet组播地址。其 中,IP组播地址标识一个组播组。由于所有IP数据包都封装在Ethernet帧中,所以还需要一个组播Ethernet地址。为使组播正常工作,主机应 能同时接收单播和组播数据,这意味着主机需要多个IP和Ethernet地址。
组播分布树有四种基本类型:泛洪法、有源树、有核树和Steiner树。
2.5 组管理协议IGMP
主机使用IGMP通知子网组播路由器,希望加入组播组;路由器使用IGMP查询本地子网中是否有属于某个组播组的主机。
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_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。
6.1 组播的可靠性
IP组播数据包典型使用用户数据报协议(UDP),而UDP是一种“尽力而为”(Best-effort)协议。因此,IP组播应用必定会遇到数据包丢失和乱序问题。
.3 网络的异构性导致组播的复杂性
Internet是一个异构网络,这种 异构性表现在很多方面。第一,Internet的低层硬件平台千差万别,可以是Ethernet、ATM、FDDI、令牌环网、帧中继、串行链路 (PSTN、xDSL)、无线网络、卫星网络、移动网络等等。这些低层网络具有不同的带宽、硬件存取控制方式、时延特征。在多链路情况下,各链路的带宽与 代价也可能不同。另外,某些网络平台的数据链路具有非对称性,比如xDSL和卫星网络。第二,主机的硬件处理能力和操作系统各不相同。就操作系统而言,主 要的操作系统,如UNIX、Windows、MacOS、OS2有不同的变种和版本,对IP组播的支持程度、进程的调度与管理、TCP/IP的实现方式和 API都有差异。第三,互连设备的差异。路由器、交换机、网络服务器在背板能力、包转发率、支持的路由协议的互操作性。这些异构性都导致在实现IP组播网 络中的复杂性。
比如:网络中不同部分的带宽不同、接收者的处理要求和处理能力不同,而所有接收者都要与同一组播源交互,这就要求采取某些方法使 得每一个接收者接收到与其接收能力和从组播源到接收者之间带宽相适合的数据流(即公平性)。再比如:ATM面向连接的特点在IP组播传输中带来了新的问 题,这使IP组播与ATM组播具有不同特点。
所以,在设计IP组播网络时,必须充分考虑到网络的异构性。
下面的代码为Sender.cpp 文件代码
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define HELLO_PORT 12345
#define HELLO_GROUP "225.0.0.37"
int main(int argc, char *argv[])
{
struct sockaddr_in addr;
int fd, cnt;
struct ip_mreq mreq;
char *message="Hello, World!";
/* create what looks like an ordinary UDP socket */
if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0)
{
perror("socket");
exit(1);
}
/* set up destination address */
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=inet_addr(HELLO_GROUP); //发送方的组播地址
addr.sin_port=htons(HELLO_PORT); //组播端口
/* now just sendto() our destination! */
while (1)
{
if (sendto(fd,message, strlen(message), 0, (struct sockaddr *) &addr, sizeof(addr)) < 0)
{
perror("sendto");
exit(1);
}
sleep(1);
}
return 0;
}
下面的代码为 Recver.cpp代码
http://www.cnblogs.com/my_life/articles/6065752.html
http://www.cnblogs.com/my_life/articles/6077569.html
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <time.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #define HELLO_PORT 12345 #define HELLO_GROUP "225.0.0.37" #define MSGBUFSIZE 256 int main(int argc, char *argv[]) { struct sockaddr_in addr; int fd, nbytes,addrlen; struct ip_mreq mreq; char msgbuf[MSGBUFSIZE]; u_int yes=1; /*** MODIFICATION TO ORIGINAL */ /* create what looks like an ordinary UDP socket */ if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0) { perror("socket"); exit(1); } /**** MODIFICATION TO ORIGINAL */ /* allow multiple sockets to use the same PORT number */ if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)) < 0) { perror("Reusing ADDR failed"); exit(1); } /*** END OF MODIFICATION TO ORIGINAL */ /* set up destination address */ memset(&addr,0,sizeof(addr)); addr.sin_family=AF_INET; addr.sin_addr.s_addr=htonl(INADDR_ANY); /* N.B.: differs from sender ,代表将会接收从任意网卡收到的数据:既能收到组播数据,又能收到单播数据*/ addr.sin_port=htons(HELLO_PORT); //组播端口 /* bind to receive address */ if (bind(fd,(struct sockaddr *) &addr,sizeof(addr)) < 0) { perror("bind"); exit(1); } /* use setsockopt() to request that the kernel join a multicast group */ mreq.imr_multiaddr.s_addr=inet_addr(HELLO_GROUP); //组播地址 mreq.imr_interface.s_addr=htonl(INADDR_ANY); //使用系统默认的网卡来收发组播数据 if (setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) < 0) //接收方才需要加入组 { perror("setsockopt"); exit(1); } /* now just enter a read-print loop */ while (1) { //ssize_t recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); addrlen=sizeof(addr); if ((nbytes=recvfrom(fd, msgbuf, MSGBUFSIZE, 0, (struct sockaddr *) &addr, (socklen_t *)&addrlen)) < 0) { perror("recvfrom"); exit(1); } puts(msgbuf); } return 0; }
下面为 编译 sender 和 recver 的Makefile 文件
CC = gcc CXX = g++ CFLAGS = -Wall -pipe -D_DEBUG -DDEBUG -g -O0 LDFLAGS = -lstdc++ RM = /bin/rm -f MODULE_INC = -I../curl-7.21.3/include -I../boost_1_45_0 -I./ MODULE_LIB = -L../boost_1_45_0/stage/lib CFLAGS += $(MODULE_INC) LDFLAGS += $(MODULE_LIB) LIBOBJS = Sender.o Recver.o TARGET = sender recver all: $(TARGET) sender: Sender.o $(CXX) -o $@ $^ $(LDFLAGS) recver: Recver.o $(CXX) -o $@ $^ $(LDFLAGS) clean: rm -f *.o rm -f $(TARGET) # make rule %.o : %.c $(CC) $(CFLAGS) -c $^ -o $@ %.o : %.cpp $(CC) $(CFLAGS) -c $^ -o $@ install: cp -f $(TARGET) ../bin/
编译好后就可以执行 ./sender 和 ./recver 查看执行的结果。
Time-To-Live (TTL) for Multicast Packets
The IP multicast routing protocol uses the Time To Live (TTL) field of IP datagrams to decide how "far" from a sending host a given multicast packet should be forwarded. The default TTL for multicast datagrams is 1, which will result in multicast packets going only to other hosts on the local network. A setsockopt (2) call may be used to change the TTL:
unsigned char ttl;
setsockopt(sock,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl));
As the values of the TTL field increase, routers will expand the number of hops they will forward a multicast packet. To provide meaningful scope control, multicast routers enforce the following "thresholds" on forwarding based on the TTL field:
- 0
- restricted to the same host
- 1
- restricted to the same subnet
- 32
- restricted to the same site
- 64
- restricted to the same region
- 128
- restricted to the same continent
- 255
- unrestricted
===========================
组播协议允许将一台主机发送的数据通过网络路由器和交换机复制到多个加入此组播的主机,是一种一对多的通讯方式。
组播协议与现在广泛使用的单播协议的不同之处在于,一个主机用单播协议向n个主机发送相同的数据时,发送主机需要分别向n个主机发送,共发送n次。
一个主机用组播协议向n个主机发送相同的数据时,只要发送1次,其数据由网络中的路由器和交换机逐级进行复制并发送给各个接收方,这样既节省服务器资源也节省网络主干的带宽资源。
与广播协议相比,只有组播接收方向路由器发出请求后,网络路由器才复制一份数据给接收方,从而节省接收方的带宽。而广播方式无论接收方是否需要,网络设备都将所有广播信息向所有设备发送,从而大量占据接收方的接入带宽。
组播协议的优势:
组播协议的优势在于当需要将大量相同的数据传输到不同主机时,
1.能节省发送数据的主机的系统资源和带宽;
2.组播是有选择地复制给有要求的主机;
3. 组播可以穿越公网广泛传播,而广播则只能在局域网或专门的广播网内部传播;
4. 组播能节省网络主干的带宽;
组播协议的缺点:
与单播协议相比,组播没有补包机制,因为组播采用的是UDP的传输方式,并且不是针对一个接受者,所以无法有针对的进行补包。所以直接组播协议传输的数据是不可靠的。
二、为什么宽带网必须使用组播协议
自从上世纪末长城宽带壮烈的宽带推广运动以来,宽带网一直面临种种问题,但这些问题归结起来就是一个问题,那就是客户端得不到与其接入带宽相称的足够的数据流。
最早的长城宽带面临的是“宽带无内容”的问题,客户得不到其承诺的视频点播等宽带娱乐,于是投诉、退户甚至诉诸法律。
电信凭借其雄厚的财力和电话线资源后来居上,但很快又面临网速慢、缺内容的投诉,电信网站上的视频点播似乎总是无尽的等待和缓冲。后来P2P软件的出现使得某些比较专业的用户似乎看到了希望,他们用BT、电驴等软件互传电影等娱乐信息也凑合了。没多久电信和网通就高举着和他们没什么关系的版权大旗封杀了BT、电驴等软件。
所有这些都是源于现在宽带网的“上下非对称”的金字塔结构,也就是网络主干的带宽远远小于所有用户带宽之和,但现在网络使用的单播通讯协议却要求网络主干的带宽等于或接近所有用户带宽之和。现在的状况是一个城市或省的网络出口主干的带宽大约相当于其所有客户带宽之和的5%,也就是说假如有5%的客户用BT软件通过网络全速传输数据,那其余95%的客户就不要玩了。现在电信主干上的流量的75%都是P2P应用的流量,已经超过了电信所能承受的极限。
那么采用CDN技术,将网络内容在城域网内就近缓冲行不行呢?答案是:技术上可行经济上行不通。其需要的服务器是一个巨大的天文数字。现在的大中城市的宽带网用户数量都在20万以上,以此数量来计算光购置CDN服务器就需要2亿元左右!这就是为什么电信不用CDN技术来满足客户需求的原因。所以在服务器的服务能力和客户机的需求上也存在着严重的上下非对称结构。
那么这个死结是不是没法解开呢?当然不是,组播协议的数据流特点就是“上下非对称”的,也就是说,在网络主干上的一条数据流通过每层交换机的复制可以变成无数客户端的数据流,形成客户端数据流之和远大于主干数据流的金字塔结构。这一特点正好与现在的网络结构相符。所以说,基于组播协议的流媒体宽带娱乐可以解决这一问题。
举例来说,使用基于组播协议的直播系统可以用一台服务器支持数万客户收看一个或几个频道的网上电视直播。假设一共提供100个频道的电视节目,每个频道是1M的MPEG4高清晰码流,则无论有1万客户还是100万客户,其占用的网络主干都是100M,而3~5台服务器硬件的投资不到100万。
当前的网络中有三种通讯模式:单播、广播、组播,其中的组播出现时间最晚但同时具备单播和广播的优点,最具有发展前景。
一、单播:
主机之间“一对一”的通讯模式,网络中的交换机和路由器对数据只进行转发不进行复制。如果10个客户机需要相同的数据,则服务器需要逐一传送,重复10次相同的工作。但由于其能够针对每个客户的及时响应,所以现在的网页浏览全部都是采用IP单播协议。网络中的路由器和交换机根据其目标地址选择传输路径,将IP单播数据传送到其指定的目的地。
单播的优点:
1. 服务器及时响应客户机的请求
2. 服务器针对每个客户不同的请求发送不通的数据,容易实现个性化服务。
单播的缺点:
1. 服务器针对每个客户机发送数据流,服务器流量=客户机数量×客户机流量;在客户数量大、每个客户机流量大的流媒体应用中服务器不堪重负。
2. 现有的网络带宽是金字塔结构,城际省际主干带宽仅仅相当于其所有用户带宽之和的5%。如果全部使用单播协议,将造成网络主干不堪重负。现在的P2P应用就已经使主干经常阻塞,只要有5%的客户在全速使用网络,其他人就不要玩了。而将主干扩展20倍几乎是不可能。
二、 广播:
主机之间“一对所有”的通讯模式,网络对其中每一台主机发出的信号都进行无条件复制并转发,所有主机都可以接收到所有信息(不管你是否需要),由于其不用路径选择,所以其网络成本可以很低廉。有线电视网就是典型的广播型网络,我们的电视机实际上是接受到所有频道的信号,但只将一个频道的信号还原成画面。在数据网络中也允许广播的存在,但其被限制在二层交换机的局域网范围内,禁止广播数据穿过路由器,防止广播数据影响大面积的主机。
广播的优点:
1. 网络设备简单,维护简单,布网成本低廉
2. 由于服务器不用向每个客户机单独发送数据,所以服务器流量负载极低。
广播的缺点:
1.无法针对每个客户的要求和时间及时提供个性化服务。
2. 网络允许服务器提供数据的带宽有限,客户端的最大带宽=服务总带宽。例如有线电视的客户端的线路支持100个频道(如果采用数字压缩技术,理论上可以提供500个频道),即使服务商有更大的财力配置更多的发送设备、改成光纤主干,也无法超过此极限。也就是说无法向众多客户提供更多样化、更加个性化的服务。
3. 广播禁止在Internet宽带网上传输。
三、组播:
主机之间“一对一组”的通讯模式,也就是加入了同一个组的主机可以接受到此组内的所有数据,网络中的交换机和路由器只向有需求者复制并转发其所需数据。主机可以向路由器请求加入或退出某个组,网络中的路由器和交换机有选择的复制并传输数据,即只将组内数据传输给那些加入组的主机。这样既能一次将数据传输给多个有需要(加入组)的主机,又能保证不影响其他不需要(未加入组)的主机的其他通讯。
组播的优点:
1. 需要相同数据流的客户端加入相同的组共享一条数据流,节省了服务器的负载。具备广播所具备的优点。
2. 由于组播协议是根据接受者的需要对数据流进行复制转发,所以服务端的服务总带宽不受客户接入端带宽的限制。IP协议允许有2亿6千多万个(268435456)组播,所以其提供的服务可以非常丰富。
3. 此协议和单播协议一样允许在Internet宽带网上传输。
组播的缺点:
1.与单播协议相比没有纠错机制,发生丢包错包后难以弥补,但可以通过一定的容错机制和QOS加以弥补。
2.现行网络虽然都支持组播的传输,但在客户认证、QOS等方面还需要完善,这些缺点在理论上都有成熟的解决方案,只是需要逐步推广应用到现存网络当中。
================================
、组播技术引入的必要性
随着宽带多媒体网络的不断发展,各种宽带网络应用层出不穷。IP TV、视频会议、数据和资料分发、网络音频应用、网络视频应用、多媒体远程教育等宽带应用都对现有宽带多媒体网络的承载能力提出了挑战。采用单播技术构建的传统网络已经无法满足新兴宽带网络应用在带宽和网络服务质量方面的要求,随之而来的是网络延时、数据丢失等等问题。此时通过引入IP组播技术,有助于解决以上问题。组播网络中,即使组播用户数量成倍增长,骨干网络中网络带宽也无需增加。简单来说,成百上千的组播应用用户和一个组播应用用户消耗的骨干网带宽是一样的,从而最大限度的解决目前宽带应用对带宽和网络服务质量的要求。
二、组播技术
1、 IP组播技术体系结构
组播协议分为主机-路由器之间的组成员关系协议和路由器-路由器之间的组播路由协议。组成员关系协议包括IGMP(互连网组管理协议)。组播路由协议分为域内组播路由协议及域间组播路由协议。域内组播路由协议包括PIM-SM、PIM-DM、DVMRP等协议,域间组播路由协议包括MBGP、MSDP等协议。同时为了有效抑制组播数据在链路层的扩散,引入了IGMP Snooping、CGMP等二层组播协议。
IGMP建立并且维护路由器直联网段的组成员关系信息。域内组播路由协议根据IGMP维护的这些组播组成员关系信息,运用一定的组播路由算法构造组播分发树进行组播数据包转发。域间组播路由协议在各自治域间发布具有组播能力的路由信息以及组播源信息,以使组播数据在域间进行转发。
2、 组播IP地址
组播IP地址用于标识一个IP组播组。IANA把D类地址空间分配给IP组播,其范围是从224.0.0.0到239.255.255.255。IP组播地址前四位均为1110。
八位组(1) 八位组(2) 八位组(3) 八位组(4)
1110XXXX XXXXXXXX XXXXXXXX XXXXXXXX
组播协议的地址在IP协议中属于D类地址。
D类地址是从224.0.0.0到239.255.255.255之间的IP地址其中224.0.0.0到224.0.0.255是被保留的地址。
组播协议的地址范围类似于一般的单播地址,被划分为两个大的地址范围,
239.0.0.0—239.255.255.255是私有地址,供各个内部网在内部使用,这个地址的组播不能上公网,类似于单播协议使用的192.168.X.X和10.X.X.X。
224.0.1.0—238.255.255.255是公用的组播地址,可以用于Internet上。
3、 组成员关系协议 (IGMP)
IGMP协议运行于主机和与主机直接相连的组播路由器之间,主机通过此协议告诉本地路由器希望加入并接受某个特定组播组的信息,同时路由器通过此协议周期性地查询局域网内某个已知组的成员是否处于活动状态
(即该网段是否仍有属于某个组播组的成员),实现所连网络组成员关系的收集与维护。
IGMP有三个版本,IGMPv1由RFC1112定义,目前通用的是IGMPv2,由RFC2236定义。IGMPv3目前仍然是一个草案。IGMPv1中定义了基本的组成员查询和报告过程,IGMPv2在此基础上添加了组成员快速离开的机制,IGMPv3中增加的主要功能是成员可以指定接收或指定不接收某些组播源的报文。这里着重介绍IGMPv2协议的功能。
IGMPv2通过查询器选举机制为所连网段选举唯一的查询器。查询器周期性的发送普遍组查询消息进行成员关系查询;主机发送报告消息来应答查询。当要加入组播组时,主机不必等待查询消息,主动发送报告消息。当要离开组播组时,主机发送离开组消息;收到离开组消息后,查询器发送特定组查询消息来确定是否所有组成员都已离开。
通过上述IGMP机制,在组播路由器里建立起一张表,其中包含路由器的各个端口以及在端口所对应的子网上都有哪些组的成员。当路由器接收到某个组G的数据报文后,只向那些有G的成员的端口上转发数据报文。至于数据报文在路由器之间如何转发则由路由协议决定,IGMP协议并不负责。
二、 关于防火墙问题
我们的建议是将组播协议的数据流旁路绕过防火墙,即连接一条不经过防火墙的链路,并在端口地址列表中只允许组播地址的数据包通过;或在防火墙内部设定透明穿透bypass,即对于组播地址的数据包不作分析处理,直接转发。其考虑基于如下几点:
1. 现在网络中使用的防火墙种类繁多、性能各异。但总体上来说对于处理视频信息这样的巨大流量都是力不从心的。
2. 组播数据流是非连接的UDP,而且需要客户机自己加入组播才能收到组播,所以发送数据者无法通过组播定位攻击,目前为止还未出现以组播为载体的病毒和黑客程序。