第8章:广播和多播(Broadcasting and Multicasting)
广播通信
一、导读
1.1 本章学习目标
第8章讲解一对多通信的两种方式——广播和多播。在前面的章节中,我们讨论的都是单播(unicast):一个发送方对一个接收方。但很多应用场景需要一对多通信:网络发现、服务公告、视频会议、软件分发等。广播和多播是TCP/IP协议族支持一对多通信的两种机制。
本章的核心学习目标包括:
- 理解广播和多播的区别:各自的优缺点和适用场景
- 掌握广播地址的类型:受限广播、定向广播、子网广播
- 深入理解多播地址的结构:D类地址、永久组地址、临时组地址
- 掌握IGMP协议的工作原理:主机如何加入/离开多播组
- 了解多播路由的基本概念:多播数据报如何在网络中传播
- 学会编写多播应用程序:套接字API的使用
1.2 为什么本章如此重要
一对多通信是Internet基础设施的重要组成部分。DHCP使用广播发现服务器,OSPF使用多播交换路由信息,视频流使用多播分发内容。理解广播和多播,对于以下方面至关重要:
- 网络管理:理解DHCP、ARP等协议为什么使用广播
- 应用开发:视频会议、直播、服务发现等应用需要多播
- 网络安全:广播风暴、多播放大攻击等安全威胁
- 网络设计:VLAN隔离广播域、多播路由协议的选择
1.3 核心问题引导
在阅读本章时,请带着以下问题思考:
广播和多播都能实现一对多通信,为什么还需要多播?广播有什么缺点?
路由器为什么不转发广播帧?如果不阻止广播,网络会变成什么样?
多播地址224.0.0.1有什么特殊含义?为什么多播路由器不转发224.0.0.x的报文?
IGMP如何知道哪些主机属于哪个多播组?为什么需要"成员查询"报文?
多播数据报的TTL有什么特殊用途?如何用TTL控制多播的范围?
为什么多播在互联网上没有大规模部署?它面临什么挑战?
二、核心概念详解
2.1 广播(Broadcast)
广播向子网内的所有主机发送数据报,无论接收方是否愿意接收。
2.1.1 广播地址类型
| 类型 | 地址 | 说明 |
|---|---|---|
| 受限广播 | 255.255.255.255 | 只在本地子网传播,路由器不转发 |
| 定向广播 | 网络地址的主机部分全1 | 向特定子网广播,路由器可转发 |
| 子网广播 | 子网的广播地址 | 向特定子网内所有主机广播 |
示例:网络 192.168.1.0/24
子网广播地址:192.168.1.255
定向广播地址:192.168.1.255(与子网广播相同)
受限广播地址:255.255.255.2552.1.2 广播的应用
- DHCP:客户端使用广播发现DHCP服务器
- ARP:使用链路层广播解析MAC地址
- 网络发现:服务公告、设备发现
- 路由协议:RIP使用广播交换路由信息
2.1.3 广播的问题
- 广播风暴:大量广播报文消耗网络带宽和主机CPU
- 安全性差:所有主机都必须处理广播报文
- 不可扩展:广播不能跨子网(路由器阻止)
- 效率低:不感兴趣的主机也会收到并处理广播
2.2 多播(Multicast)
多播只向加入特定组的主机发送数据报,比广播更高效。
2.2.1 多播地址
多播使用D类IP地址(224.0.0.0 - 239.255.255.255):
| 地址范围 | 用途 |
|---|---|
| 224.0.0.0 - 224.0.0.255 | 本地子网保留(不路由) |
| 224.0.1.0 - 238.255.255.255 | 全局多播(可路由) |
| 239.0.0.0 - 239.255.255.255 | 管理范围多播 |
常见的永久多播组地址:
| 地址 | 用途 |
|---|---|
| 224.0.0.1 | 所有主机 |
| 224.0.0.2 | 所有路由器 |
| 224.0.0.5 | OSPF路由器 |
| 224.0.0.9 | RIPv2路由器 |
| 224.0.0.22 | IGMP |
| 224.0.0.251 | mDNS |
2.2.2 多播的MAC地址映射
多播IP地址需要映射到多播MAC地址。映射规则:
多播MAC = 01:00:5E + IP地址的低23位例如:
多播IP: 224.0.1.1
IP二进制: 1110 0000.0000 0000.0000 0001.0000 0001
低23位: 0 0000 0000.0000 0001.0000 0001
多播MAC: 01:00:5E:00:01:01注意:IP地址有28位多播标识,但MAC只使用低23位,存在32:1的映射重叠。
2.2.3 多播的优势
与广播相比,多播有以下优势:
- 高效:只有组成员接收数据,非成员不处理
- 可扩展:可以跨子网传播(需要多播路由支持)
- 灵活:主机可以动态加入/离开多播组
2.3 IGMP(Internet组管理协议)
IGMP(Internet Group Management Protocol)管理多播组成员关系。主机通过IGMP通知本地路由器它想加入哪个多播组。
2.3.1 IGMP版本
| 版本 | 特性 |
|---|---|
| IGMPv1 | 基本加入/离开 |
| IGMPv2 | 增加离开组报文、成员查询 |
| IGMPv3 | 支持源过滤(SSM) |
2.3.2 IGMPv2工作流程
加入多播组:
主机发送IGMP成员报告(Membership Report),声明加入某个多播组
本地路由器收到后,开始转发该组的多播数据
维护组成员:
路由器定期发送成员查询(Membership Query)
每个多播组选举一个"报告者"(reporter)响应
报告者发送成员报告,确认组仍然有成员
离开多播组:
主机发送离开组报文(Leave Group)
路由器发送特定组查询,确认是否还有其他成员
如果没有响应,路由器停止转发该组的数据
# 查看本机的IGMP组成员信息
cat /proc/net/igmp
# 使用tcpdump观察IGMP报文
sudo tcpdump -i eth0 -nn 'igmp'2.4 多播路由
多播数据报需要在网络中高效传播,避免重复传输。多播路由使用反向路径转发(RPF)和多播分发树来实现。
2.4.1 反向路径转发(RPF)
路由器收到多播数据报时,检查数据报是否从"正确"的接口到达(即朝向源的最短路径接口)。如果是,则转发到所有有组成员的接口;如果不是,则丢弃。
2.4.2 多播路由协议
| 协议 | 范围 | 算法 |
|---|---|---|
| DVMRP | 小型网络 | 距离向量 |
| MOSPF | 中型网络 | 链路状态 |
| PIM-SM | 大型网络 | 共享树+源树 |
| PIM-DM | 大型网络 | 泛洪剪枝 |
PIM-SM(Protocol Independent Multicast - Sparse Mode)是目前最广泛使用的多播路由协议。
2.5 多播套接字编程
在应用程序中使用多播需要特殊的套接字选项:
#include <sys/socket.h>
#include <netinet/in.h>
// 加入多播组
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr("224.1.1.1");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
// 设置多播TTL
int ttl = 32;
setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
// 设置多播接口
struct in_addr iface;
iface.s_addr = inet_addr("192.168.1.100");
setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &iface, sizeof(iface));
// 发送多播数据
struct sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("224.1.1.1");
dest.sin_port = htons(5000);
sendto(sockfd, buffer, len, 0, (struct sockaddr *)&dest, sizeof(dest));三、实战案例
3.1 案例一:观察IGMP报文
sudo tcpdump -i eth0 -nn 'igmp'输出示例:
12:34:56.789 IP 192.168.1.100 > 224.0.0.22: igmp v2 report 224.1.1.1
12:34:57.789 IP 192.168.1.1 > 224.0.0.1: igmp query - group 224.1.1.13.2 案例二:编写多播接收程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
int main() {
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
local_addr.sin_port = htons(5000);
bind(sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr));
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr("224.1.1.1");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
char buffer[1024];
while (1) {
int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL);
if (n > 0) {
buffer[n] = '\0';
printf("Received: %s\n", buffer);
}
}
return 0;
}3.3 案例三:使用iperf测试多播性能
# 服务端(接收多播)
iperf -s -u -B 224.1.1.1 -i 1
# 客户端(发送多播)
iperf -c 224.1.1.1 -u -T 32 -t 10 -i 1四、常见误区与难点
4.1 误区一:"多播就是广播"
多播只向组成员发送数据,广播向所有主机发送。多播更高效,但需要IGMP和多播路由支持。
4.2 误区二:"多播在互联网上广泛使用"
实际上,多播在互联网上的部署非常有限。大多数ISP不支持多播路由。多播主要用于企业内网、IPTV、金融数据分发等受控环境。
4.3 难点:理解RPF检查
RPF检查确保多播数据报不会形成环路。它检查数据报是否从朝向源的正确接口到达。理解RPF需要理解单播路由表如何影响多播转发。
五、本章小结
第8章讲解了广播和多播:
广播向子网内所有主机发送数据,使用广播地址(如255.255.255.255)。简单但效率低,不可扩展。
多播只向组成员发送数据,使用D类地址(224.0.0.0-239.255.255.255)。高效但需要IGMP和多播路由支持。
IGMP管理多播组成员关系,主机通过IGMP加入/离开多播组。
多播路由使用RPF检查和分发树实现高效的多点传输。
多播编程使用特殊的套接字选项(IP_ADD_MEMBERSHIP等)。
六、思考与练习
思考题
如果让你设计一个直播系统,你会选择广播还是多播?为什么?在现代互联网环境下,有什么替代方案?
为什么多播IP到MAC的映射存在32:1的重叠?这会导致什么问题?如何解决?
为什么互联网上没有大规模部署多播?技术障碍和商业障碍分别是什么?
练习题
编写一个多播发送程序和一个多播接收程序,测试多播通信。
使用tcpdump观察IGMP报文,分析加入和离开多播组的过程。
使用iperf对比广播和多播在不同接收者数量下的性能差异。