组播、单播和广播的定义
单播、组播和广播都是IP报文网络传输的三种模式,它们的定义如下:
- 单播 是主机间一对一的通讯模式,网络中的设备根据网络报文中包含的目的地址选择传输路径,将单播报文传送到指定的目的地,只对接收到的数据进行转发,不会进行复制。它能够针对每台主机及时的响应,现在的网页浏览全部都是采用单播模式。
- 广播 是主机间一对所有的通讯模式,设备会将报文发送到网络中的所有可能接收者。设备简单地将它收到的任何广播报文都复制并转发到除该报文到达的接口外的每个接口。广播处理流程简单,不用选择路径。
- 组播 是主机间一对多的通讯模式, 组播是一种允许一个或多个组播源发送同一报文到多个接收者的技术。组播源将一份报文发送到特定的组播地址,组播地址不同于单播地址,它并不属于特定某个主机,而是属于一组主机。一个组播地址表示一个群组,需要接收组播报文的接收者都加入这个群组。
用户主机如何区分组播、单播和广播
我们前面了解了单播、组播和广播的定义,但对于主机用户是怎样识别单播、组播和广播的呢?
用户主机是通过识别IP地址,区分IP传输的方式。IP地址被分为三类,分别是:
- 单播IP地址:一个单播IP地址只能标识一台用户主机。一份使用单播IP地址为目的地址的IP报文,只能被一台用户主机接收。
- 广播IP地址:一个广播IP地址能够标识某确定网段内的所有用户主机。一份使用广播IP地址为目的地址的IP报文,能够被该网段内的所有用户主机接收。IP广播报文不能跨网段传播。
- 组播IP地址:一个组播IP地址能够标识网络不同位置的多个用户主机,一台用户主机可以同时识别多个组播IP地址。一份使用组播IP地址为目的地址的IP报文,能够被网络不同位置的多个用户主机接收。
用户主机使用不同的类别的IP地址,就选用了不同的传输方式:
IP报文网络传输使用单播IP地址的,简称为单播。
IP报文网络传输使用广播IP地址的,简称为广播。
IP报文网络传输使用组播IP地址的,简称为组播。
组播、单播和广播的优缺点
传输方式 | 优势 | 不足 |
单播方式 | 1. 一份单播报文,使用一个单播地址作为目的地址。Source向每个Receiver地址发送一份独立的单播报文。如果网络中存在N个接收者,则组播源需要发送N份单播报文。 2. 网络为每份单播报文执行独立的数据转发,形成一条独立的数据传送通路。N份单播报文形成N条相互独立的传输路径。 | 1. 单播方式下,网络中传输的信息量和需求该信息的用户量成正比,当需求该信息的用户量较大时,网络中将出现多份相同信息流,不仅占用处理器资源而且浪费带宽。 2. 单播方式较适合用户稀少的网络,当用户量较大时很难保证网络传输质量。 |
广播方式 | 1.一份广播报文,使用一个广播地址作为目的地址。Source向本网段对应的广播地址发送且仅发送一份报文。 2. 不管是否有需求,保证报文被网段中的所有用户主机接收。 | 1.广播方式下,信息发送者与用户主机被限制在一个共享网段中,且该网段所有用户主机都能接收到该信息,这样会导致无信息需求的主机也收到该信息,网络中流量有冗余。 2. 广播方式只适合共享网段,且信息安全性和有偿服务得不到保障。 |
组播方式 | 1.一份组播报文,使用一个组播地址作为目的地址。Source(组播源)向一个组播地址发送且仅发送一份报文。网络中部署的组播协议为此组播报文建立一棵树型路由,根是Source,叶子是所有组播组成员。 2. 组播方式下,单一的信息流沿树型路径被同时发送给一组用户,相同的组播数据流在每一条链路上最多仅有一份。相比单播来说,使用组播方式传递信息,用户的增加不会显著增加网络的负载,减轻了服务器和CPU的负荷。 3. 组播报文可以跨网段传输,不需要此报文的用户不能收到此报文。相比广播来说,使用组播方式可以远距离传输信息,且只将信息传输到有接收者的地方,保障了信息的安全性。 4. 组播技术有效地解决了单点发送多点接收的问题,实现了IP网络中点到多点的高效数据传送。 | 1.组播协议实现是基于UDP的因此组播数据的丢失和乱序存在问题; 2. 组播数据的安全性问题,因为其可以加入组中获取数据,因此存在数据流被监听的隐患,需要引入新的机制来进行用户认证校验。 |
组播的实现机制
文章 组播(多播) 中写的很详细,对组播中用到的协议进行了较为细致的介绍,就不再啰嗦了。
组播通信的前提要求
- 全网设备相连的接口都需要配置单播IP地址
- 全网要部署单播路由协议,确保数据源和多用户之间路由互通
- 全网部署组播PIM协议(PIM-SM或者PIM-DM),并配置相应的BSR和RP。
- 多用户必须发送IGMP组加入报文,组加入报文中包含其需要加入的组IP地址,例如地址:225.1.1.1。
组播实现机制总体来说就是接收者告诉一个中心节点(在组播协议里面称为RP),它需要哪些组地址的流量;RP需要被告之数据源(在组播场景中我们称之为组播源,它的特点是流量二层头中目的MAC地址是组播MAC地址,IP报层IP报文头目的地址是组播IP地址)在哪,数据源往哪些组地址发流。RP知道了接收方和发送方的所有信息后,就会根据需要把流量发送到特定位置(它发送的过程中建立了组播分发树)。
组播示例
发送客户端:
#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;
}
接受服务端:
#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;
}
参考文章