《TCP/IP网络编程》学习笔记 | Chapter 14:多播与广播
《TCP/IP网络编程》学习笔记 | Chapter 14:多播与广播
多播
多播(Multicast)方式的数据传输是基于UDP完成的,可以同时向多个主机传递数据。
多播常用于视频会议、在线直播、IPTV等场景,其中数据需要发送给多个特定的接收者而不是所有人。
多播的数据传输方式和特点
多播的特点:
- 多播服务器针对特定的多播组,只发送一次数据。
- 即使只发送一次数据,该组内所有客户端都会接受到数据。
- 多播组数可以在IP地址范围内任意增加。
- 加入多播组就能收到发往该多播组的数据。
多播组时D类IP地址,即224.0.0.0~239.255.255.255,并被划分为三类:
- 局部链接多播地址:224.0.0.0~224.0.0.255,这是为路由协议和其它用途保留的地址,路由器并不转发属于此范围的IP包。
- 预留多播地址:224.0.1.0~238.255.255.255,可用于全球范围(如Internet)或网络协议。
- 管理权限多播地址:239.0.0.0~239.255.255.255,可供组织内部使用,类似于私有IP地址,不能用于Internet,可限制多播范围。
「加入多播组」可以理解为通过程序完成如下声明:在 D 类IP地址中,我希望接收发往目标 239.234.218.234 的多播数据。
多播技术基于UDP传输,向网络传输1个多播数据包时,路由器会复制该数据包并传递到多个主机,如下图所示:
多播的传输需要借助路由器完成。若通过 TCP 或 UDP 向 1000 个主机发送文件,则共需要传递 1000 次。但是此时如果用多播网络传输文件,则只需要发送一次。正是由于这样的特性,大大节省了网络流量,减少了占用带宽,同时也减少了发送端的重复无用的工作,多播主要用于“多媒体数据的实时传输”。
要实现多播通信,要求介于多播源和接收者之间的路由器、集线器、交换机以及主机均需支持IP多播。目前,IP多播技术已得到硬件、软件厂商的广泛支持。
然而,有些路由器并不支持多播,或即便支持也因网络拥堵问题故意阻断多播。因此,为了在不支持多播的路由器中完成多播通信,也会使用隧道(Tunneling)技术。
路由(Routing)和 TTL(Time to Live,生存时间),以及加入组的办法
为了传递多播数据包,必须设置 TTL ,它是决定「数据包传递距离」的主要因素。TTL 用整数表示,并且每经过一个路由器就减一。TTL 变为 0 时,该数据包就无法再被传递,只能销毁。因此,TTL 的值设置过大将影响网络流量。当然,设置过小,也无法传递到目标。
缺省情况下,发送 IP 多播数据报时其 TTL 值为 1。与设置 TTL 相关的协议层为 IPPROTO_IP ,选项名为 IP_MULTICAST_TTL。这些阈值将针对具有以下初始 TTL 值的多播数据报强制实施相应约定:
- 0:限定在同一主机
- 1:限定在同一子网
- 32:限定在同一站点
- 64:限定在同一地区
- 128:限定在同一洲
- 255:范围不受限制
站点和地区并未严格定义,站点可以根据实际情况再分为更小的管理单元。
因此,可以用如下代码把 TTL 设置为 64:
int send_sock;
int time_live = 64;
...
send_sock = socket(PF_INET, SOCK_DGRAM, 0);
setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&time_live, sizeof(time_live));
...
加入多播组也通过设置套接字可选项来完成。加入多播组相关的协议层为 IPPROTO_IP,选项名为 IP_ADD_MEMBERSHIP 。可通过如下代码加入多播组:
int recv_sock;
struct ip_mreq join_adr;
...
recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
...
join_adr.imr_multiaddr.s_addr = "多播组地址信息";
join_adr.imr_interface.s_addr = "加入多播组的主机地址信息";
setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&join_adr, sizeof(join_adr));
...
下面是 ip_mreq 结构体的定义:
struct ip_mreq
{
struct in_addr imr_multiaddr; // 写入加入组的IP地址
struct in_addr imr_interface; // 加入该组的套接字所属主机的IP地址
};
有关多播的实现需要设置UDP套接字的一些可选项,以表格的形式进行汇总:
IPPROTO_IP 选项名 | 说明 | 数据类型 |
---|---|---|
IP_MULTICAST_TTL | 生存时间(Time To Live),组播传送距离 | int |
IP_ADD_MEMBERSHIP | 加入组播 | struct ip_mreq |
IP_DROP_MEMBERSHIP | 离开组播 | struct ip_mreq |
IP_MULTICAST_IF | 获取默认接口或设置接口 | int |
IP_MULTICAST_LOOP | 组播数据回送,缺省默认回送 | int |
实现多播 Sender 和 Receiver
多播中用「发送者」(以下称为 Sender) 和「接收者」(以下称为 Receiver)替代服务器端和客户端。顾名思义,此处的 Sender 是多播数据的发送主体,Receiver 是需要多播组加入过程的数据接收主体。下面是示例,示例的运行场景如下:
- Sender : 向 AAA 组广播(Broadcasting)文件中保存的新闻信息
- Receiver : 接收传递到 AAA 组的新闻信息。
news_sender.c 程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define TTL 64
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc, char *argv[])
{
int send_sock;
struct sockaddr_in mul_adr;
int time_live = TTL;
FILE *fp;
char buf[BUF_SIZE];
if (argc != 3)
{
printf("Usage : %s <GroupIP> <PORT>\n", argv[0]);
exit(1);
}
send_sock = socket(PF_INET, SOCK_DGRAM, 0); // 创建 UDP 套接字
memset(&mul_adr, 0, sizeof(mul_adr));
mul_adr.sin_family = AF_INET;
mul_adr.sin_addr.s_addr = inet_addr(argv[1]); // 必须将IP地址设置为多播地址
mul_adr.sin_port = htons(atoi(argv[2]));
// 指定套接字中 TTL 的信息
setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&time_live, sizeof(time_live));
if ((fp = fopen("news.txt", "r")) == NULL)
error_handling("fopen() error");
while (!feof(fp)) // 如果文件没结束就返回0
{
fgets(buf, BUF_SIZE, fp);
sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr *)&mul_adr, sizeof(mul_adr));
sleep(2);
}
fclose(fp);
close(send_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
news_receiver.c 程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc, char *argv[])
{
int recv_sock;
int str_len;
char buf[BUF_SIZE];
struct sockaddr_in adr;
struct ip_mreq join_adr;
if (argc != 3)
{
printf("Usage : %s <GroupIP> <PORT>\n", argv[0]);
exit(1);
}
recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
memset(&adr, 0, sizeof(adr));
adr.sin_family = AF_INET;
adr.sin_addr.s_addr = htonl(INADDR_ANY);
adr.sin_port = htons(atoi(argv[2]));
if (bind(recv_sock, (struct sockaddr *)&adr, sizeof(adr)) == -1)
error_handling("bind() error");
// 初始化结构体
join_adr.imr_multiaddr.s_addr = inet_addr(argv[1]); // 多播组地址
join_adr.imr_interface.s_addr = htonl(INADDR_ANY); // 待加入的IP地址
// 利用套接字选项 IP_ADD_MEMBERSHIP 加入多播组,完成了接受指定的多播组数据的所有准备
setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&join_adr, sizeof(join_adr));
while (1)
{
// 通过 recvfrom 函数接受多播数据。如果不需要知道传输数据的主机地址信息,可以向recvfrom函数的第5 6参数分贝传入 NULL 0
str_len = recvfrom(recv_sock, buf, BUF_SIZE - 1, 0, NULL, 0);
if (str_len < 0)
break;
buf[str_len] = 0;
fputs(buf, stdout);
}
close(recv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
通过结果可以看出,使用 sender 多播信息,通过 receiver 接收广播,如果延迟运行 receiver 将无法接受之前发送的信息。
广播
广播(Broadcast)在「一次性向多个主机发送数据」这一点上与多播类似,但传输数据的范围有区别。多播即使在跨越不同网络的情况下,只要加入多播组就能接受数据。相反,广播只能向同一网络中的主机传输数据。
广播的理解和实现方法
广播是向同一网络中的所有主机传输数据的方法。与多播相同,广播也是通过 UDP 来完成的。根据传输数据时使用的IP地址形式,广播分为以下两种:
- 直接广播(Directed Broadcast):向特定区域内的所有主机传输数据。广播的IP地址除了网络地址外,其余主机地址全部设置为1,例如希望向 192.168.1.32 所在网络中的所有主机发送广播数据时可以向 192.168.1.255 传输。
- 本地广播(Local Broadcast):本地广播的IP地址限定为 255.255.255.255,向该主机所在网络的所有主机发送广播数据。例如,192.32.24 网络中的主机向 255.255.255.255 传输数据时,数据将传输到 192.32.24 网络中所有主机。
有关广播的实现需要设置UDP套接字的一些可选项:
SOL_SOCKET 选项名 | 说明 | 数据类型 |
---|---|---|
SO_BROADCAST | 允许或禁止发送广播数据(1启用,0不启用) | int |
数据通信中使用的IP地址是与 UDP 示例的唯一区别。默认生成的套接字会阻止广播,因此,只需通过如下代码更改默认设置:
int send_sock;
send_sock = socket(AF_INET, SOCK_DGRAM, 0); // 使用UDP传输
int opt = 1; // 启用广播
setsockopt(send_sock, SOL_SOCKET, SOL_SOCKET, (void *)&opt, sizeof(opt));
实现广播数据的 Sender 和 Receiver
news_sender_brd.c 程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc, char *argv[])
{
int send_sock;
struct sockaddr_in broad_adr;
FILE *fp;
char buf[BUF_SIZE];
int so_brd = 1;
if (argc != 3)
{
printf("Usage : %s <GroupIP> <PORT>\n", argv[0]);
exit(1);
}
send_sock = socket(PF_INET, SOCK_DGRAM, 0); // 创建 UDP 套接字
memset(&broad_adr, 0, sizeof(broad_adr));
broad_adr.sin_family = AF_INET;
broad_adr.sin_addr.s_addr = inet_addr(argv[1]);
broad_adr.sin_port = htons(atoi(argv[2]));
setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (void *)&so_brd, sizeof(so_brd));
if ((fp = fopen("news.txt", "r")) == NULL)
error_handling("fopen() error");
while (!feof(fp)) // 如果文件没结束就返回0
{
fgets(buf, BUF_SIZE, fp);
sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr *)&broad_adr, sizeof(broad_adr));
sleep(2);
}
fclose(fp);
close(send_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
news_receiver_brd.c 程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc, char *argv[])
{
int recv_sock;
int str_len;
char buf[BUF_SIZE];
struct sockaddr_in adr;
if (argc != 2)
{
printf("Usage : %s <PORT>\n", argv[0]);
exit(1);
}
recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
memset(&adr, 0, sizeof(adr));
adr.sin_family = AF_INET;
adr.sin_addr.s_addr = htonl(INADDR_ANY);
adr.sin_port = htons(atoi(argv[1]));
if (bind(recv_sock, (struct sockaddr *)&adr, sizeof(adr)) == -1)
error_handling("bind() error");
while (1)
{
// 通过 recvfrom 函数接受数据。如果不需要知道传输数据的主机地址信息,可以向recvfrom函数的第5 6参数分贝传入 NULL 0
str_len = recvfrom(recv_sock, buf, BUF_SIZE - 1, 0, NULL, 0);
if (str_len < 0)
break;
buf[str_len] = 0;
fputs(buf, stdout);
}
close(recv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
基于 Windows 的实现
在Windows平台中,该技术的套接字设置没有区别,只需要修改对应的变量和头文件即可。
基于 Windows 的多播示例程序
news_sender_win.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#define TTL 64
#define BUF_SIZE 30
void ErrorHanding(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char *argv[])
{
WSADATA wsaData;
SOCKET sendSock;
SOCKADDR_IN mulAddr;
int timeLive = TTL;
char buf[BUF_SIZE];
if (argc != 3)
{
printf("Usage: %s <GroupIP> <port>\n", argv[0]);
exit(1);
}
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
ErrorHanding("WSAStartup() error!");
sendSock = socket(PF_INET, SOCK_DGRAM, 0);
if (sendSock == INVALID_SOCKET)
ErrorHanding("socket() error!");
memset(&mulAddr, 0, sizeof(mulAddr));
mulAddr.sin_family = AF_INET;
mulAddr.sin_addr.s_addr = inet_addr(argv[1]);
mulAddr.sin_port = htons(atoi(argv[2]));
setsockopt(sendSock, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&timeLive, sizeof(timeLive));
FILE *fp = fopen("news.txt", "r");
if (fp == NULL)
ErrorHanding("fopen() error!");
while (!feof(fp))
{
fgets(buf, BUF_SIZE, fp);
sendto(sendSock, buf, strlen(buf), 0, (SOCKADDR *)&mulAddr, sizeof(mulAddr));
Sleep(2000);
}
closesocket(sendSock);
WSACleanup();
return 0;
}
news_receiver_win.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#define BUF_SIZE 30
void ErrorHanding(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char *argv[])
{
WSADATA wsaData;
SOCKET recvSock;
SOCKADDR_IN recvAddr;
struct ip_mreq joinAddr;
int strLen;
char buf[BUF_SIZE];
if (argc != 3)
{
printf("Usage: %s <GroupIP> <port>\n", argv[0]);
exit(1);
}
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
ErrorHanding("WSAStartup() error!");
recvSock = socket(PF_INET, SOCK_DGRAM, 0);
if (recvSock == INVALID_SOCKET)
ErrorHanding("socket() error!");
memset(&recvAddr, 0, sizeof(recvAddr));
recvAddr.sin_family = AF_INET;
recvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
recvAddr.sin_port = htons(atoi(argv[2]));
if (bind(recvSock, (SOCKADDR *)&recvAddr, sizeof(recvAddr)) == SOCKET_ERROR)
ErrorHanding("bind() error!");
joinAddr.imr_multiaddr.s_addr = inet_addr(argv[1]);
joinAddr.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(recvSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&joinAddr, sizeof(joinAddr)) == SOCKET_ERROR)
ErrorHanding("setsocket() error!");
while (1)
{
strLen = recvfrom(recvSock, buf, BUF_SIZE - 1, 0, NULL, 0);
if (strLen < 0)
break;
buf[strLen] = '\0';
fputs(buf, stdout);
}
closesocket(recvSock);
WSACleanup();
return 0;
}
编译:
gcc news_sender_win.c -lwsock32 -o newsSendWin
gcc news_receiver_win.c -lwsock32 -o newsRecvWin
运行结果:
// 接收端
C:\Users\81228\Documents\Program\TCP IP Project\Chapter 14>newsRecvWin 224.1.1.2 9190
A powerful magnitude-7.7 earthquake struck central Myanmar on Friday, causing widespread devastation. The confirmed death toll from the earthquake has risen to 1,644.
// 发送端
C:\Users\81228\Documents\Program\TCP IP Project\Chapter 14>newsSendWin 224.1.1.2 9190
基于 Windows 的本地广播示例程序
news_sender_local_broad_win.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#define BUF_SIZE 30
void ErrorHanding(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char *argv[])
{
WSADATA wsaData;
SOCKET sendSock;
SOCKADDR_IN localBroadAddr;
int brd = 1;
char buf[BUF_SIZE];
if (argc != 3)
{
printf("Usage: %s <GroupIP> <port>\n", argv[0]);
exit(1);
}
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
ErrorHanding("WSAStartup() error!");
sendSock = socket(PF_INET, SOCK_DGRAM, 0);
if (sendSock == INVALID_SOCKET)
ErrorHanding("socket() error!");
memset(&localBroadAddr, 0, sizeof(localBroadAddr));
localBroadAddr.sin_family = AF_INET;
localBroadAddr.sin_addr.s_addr = inet_addr(argv[1]);
localBroadAddr.sin_port = htons(atoi(argv[2]));
// 广播
setsockopt(sendSock, SOL_SOCKET, SO_BROADCAST, (void *)&brd, sizeof(brd));
FILE *fp = fopen("news.txt", "r");
if (fp == NULL)
ErrorHanding("fopen() error!");
while (!feof(fp))
{
fgets(buf, BUF_SIZE, fp);
sendto(sendSock, buf, strlen(buf), 0, (SOCKADDR *)&localBroadAddr, sizeof(localBroadAddr));
Sleep(2000);
}
closesocket(sendSock);
WSACleanup();
return 0;
}
news_receiver_local_broad_win.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#define BUF_SIZE 30
void ErrorHanding(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char *argv[])
{
WSADATA wsaData;
SOCKET recvSock;
SOCKADDR_IN recvAddr;
int strLen;
char buf[BUF_SIZE];
if (argc != 2)
{
printf("Usage: %s <port>\n", argv[0]);
exit(1);
}
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
ErrorHanding("WSAStartup() error!");
recvSock = socket(PF_INET, SOCK_DGRAM, 0);
if (recvSock == INVALID_SOCKET)
ErrorHanding("socket() error!");
memset(&recvAddr, 0, sizeof(recvAddr));
recvAddr.sin_family = AF_INET;
recvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
recvAddr.sin_port = htons(atoi(argv[1]));
if (bind(recvSock, (SOCKADDR *)&recvAddr, sizeof(recvAddr)) == SOCKET_ERROR)
ErrorHanding("bind() error!");
while (1)
{
strLen = recvfrom(recvSock, buf, BUF_SIZE - 1, 0, NULL, 0);
if (strLen < 0)
break;
buf[strLen] = '\0';
fputs(buf, stdout);
}
closesocket(recvSock);
WSACleanup();
return 0;
}
编译:
gcc news_sender_local_broad_win.c -lwsock32 -o newsSendLocalBroadWin
gcc news_receiver_local_broad_win.c -lwsock32 -o newsRecvLocalBroadWin
运行结果:
// 接收端
C:\Users\81228\Documents\Program\TCP IP Project\Chapter 14>newsRecvLocalBroadWin 9190
A powerful magnitude-7.7 earthquake struck central Myanmar on Friday, causing widespread devastation. The confirmed death toll from the earthquake has risen to 1,644.
// 发送端
C:\Users\81228\Documents\Program\TCP IP Project\Chapter 14>newsSendLocalBroadWin 255.255.255.255 9190
基于 Windows 的直接广播示例程序
前一小节的程序不用变,我们将本地广播地址改成自己主机的地址。
按下 win + R,输入 cmd,进入,输入 ipconfig 指令,就能找到自己主机的 IPv4 地址。
再次运行程序,修改发送端指定的发送 IP 地址为我们主机的地址。
运行结果:
// 接收端
C:\Users\81228\Documents\Program\TCP IP Project\Chapter 14>newsRecvLocalBroadWin 9190
A powerful magnitude-7.7 earthquake struck central Myanmar on Friday, causing widespread devastation. The confirmed death toll from the earthquake has risen to 1,644.
// 发送端
C:\Users\81228\Documents\Program\TCP IP Project\Chapter 14>newsSendLocalBroadWin 192.168.113.1 9190
多播 VS 广播
对比
- 接收者范围: 广播发送给所有设备,而多播只发送给特定的组。
- 网络效率: 多播比广播更高效,因为它减少了不必要的数据传输。
- 地址类型: 广播使用广播地址,多播使用多播地址。
- 适用场景: 广播适用于需要通知所有设备的场景,而多播适用于需要向特定群体发送数据的场景。
多播的使用场景
- 大规模数据分发: 股票市场信息、新闻更新或在线直播。
- 视频会议和在线教育
- IPTV和多媒体流
- 网络监控和安全
- 分布式计算
- 实时数据服务: 天气更新、交通信息或金融市场数据,可以多播给所有订阅服务的用户。
广播的使用场景
- 局域网内通信: 如打印作业请求或网络发现协议。
- 设备发现: 一些设备和服务使用广播来发现网络上的其他设备。
- 游戏和娱乐: 在局域网游戏或多媒体应用中,广播可以用于快速发现和连接游戏服务器或媒体播放设备。
- 紧急通知系统: 在某些情况下,可能需要向局域网内的所有设备发送紧急通知或警报。
习题
(1)TTL的含义是什么?请从路由器的角度说明较大的TTL值与较小的TTL值之间的区别及问题。
TTL是Time to Live的简写,意思是生存时间,是决定“数据包传递距离”的主要因素。
TTL的值设置过大将影响网络流量。当然,设置过小,也无法传递到目标。
(2)多播与广播的异同点是什么?请从数据通信的角度说明。
广播在“一次性向多个主机发送数据”这一点上与多播类似,但传输数据的范围有区别,多播即使在跨越不同网络的情况下,只要加入多播组就能接收数据,相反,广播只能向同一网络中的主机传输数据。
(3)下列关于多播的描述错误的是?
a. 多播是用来向加入多播组的所有主机传输数据的协议。
b. 主机连接到同一网络才能加入多播组,也就是说,多播组无法跨越多个网络。
c. 能够加入多播组的主机数并无限制,但只能有1个主机(Sender)向该组发送数据。
d. 多播时使用的套接字是UDP套接字,因为多播是基于UDP进行数据通信的。
答:b、c。
(4)多播也对网络流量有利,请比较TCP数据交换方式解释其原因。
若通过TCP向1000个主机发送文件,共需传递1000次,但此时若使用多播方式传输文件,则是需要发送1次,之后由路由器进行转发。
(5)多播方式的数据通信需要MBone虚拟网络。换言之, MBone是用于多播网络,但它是虚拟网络。请解释此处的“虚拟网络”。
“虚拟网络”是指一个不依赖于实际物理网络硬件的网络拓扑,它通过在现有物理网络上叠加逻辑结构和功能来实现特定目的。在多播通信中,MBone(Multicast Backbone)作为虚拟网络,起到了支持多播数据传输的作用。
具体来说,MBone的“虚拟网络”特性包含以下几点:
-
建立在现有的物理网络之上:
MBone并不是一个独立的物理网络,而是通过软件配置和协议(如多播路由协议)在现有的IP网络(如互联网)中创建的逻辑网络。 -
逻辑拓扑:
MBone使用一组支持多播的路由器(通常通过多播路由协议如 DVMRP 或 PIM)来构建虚拟链路。数据在这些逻辑链路之间传递,而不必依赖物理链路的具体布局。 -
多播组成员动态加入和离开:
通过MBone,用户可以动态加入或离开一个多播组,这种灵活性由虚拟网络的逻辑实现,而不是由物理硬件决定。 -
支持多播流量的转发和优化:
多播通常需要将一份数据高效地分发到多个目的地。MBone的虚拟网络通过在逻辑层次管理数据包的转发,避免了在物理网络中传输过多的冗余数据。
总结:MBone被称为虚拟网络,是因为它依赖现有的物理网络,通过软件和协议来构建支持多播通信的逻辑网络。它为多播通信提供了必要的基础设施,而无需物理网络的重新部署或重大改造。