按接收端数量只有三类:一对一、一对全、一对多。 组播的语义是「订阅」而不是「投递」——发送方根本不知道有谁在听。
1.7.1 与 1.7.2 讲的是「怎么传」,这一节讲「传给谁」。 广播与组播的分界在路由器,这一点最容易搞混。
一、三类发法
一张表说清:
| 类型 | 发给谁 | 典型地址 |
|---|---|---|
| 单播 | 一个确定地址 | 192.168.3.1 |
| 广播 | 本网段全段 | 192.168.3.255 |
| 组播 | 一群订阅者 | 239.255.255.250 |
单播占了互联网流量的绝大多数:你打开一个网页、看一个视频,本质都是「一对一」。 视频网站对一万人直播,服务端发的是一万份单播——除非它用了组播或 CDN 分发。 第二行那个地址最后一位是 255,这就是「本网段广播地址」: 它的算法是「网络部分不变、主机部分全 1」。 所以 192.168.3.0/24 的广播地址是 192.168.3.255,这个不是随便定的,是算出来的。
二、组播是订阅,不是投递
这一点是组播最难理解也最关键的地方:
| 谁的动作 | 做什么 | 知道对方吗 |
|---|---|---|
| 发送方 | 往组地址发包 | 完全不知道有谁在听 |
| 接收方 | 主动加入组 | 知道组地址 |
| 路由器 | 按成员表转发 | 知道哪个口下有成员 |
第一行解释了组播的「无连接」性质:发送方只往一个地址发,不关心有没有人收。 没人收也不报错—— 这就是为什么「自发自收」的实验能成:本机加入了那个组,自己发的包自己也能收到。 第三行是组播能跨网段的原因:路由器要知道「哪个口下面有成员」,这靠 IGMP 协议维护。 没有 IGMP,路由器不知道该往哪转,组播就出不了本网段。 对比一下广播: 广播是「往本网段所有设备发」,路由器默认不转发——所以广播永远出不了本网段。 这也是「设备发现」这类功能偏爱组播的原因:它至少有机会跨过一个路由器。
三、为什么广播不能跨路由
不是技术上做不到,是「不敢」:
| 如果广播能跨路由 | 后果 |
|---|---|
| 全网泛洪 | 一台设备的广播打满所有链路 |
| 放大效应 | 一个包变成 N 个包 |
| 故障扩散 | 局部问题变成全局问题 |
第一行是最直接的: 广播的本质是「发给所有人」,一旦允许跨网段,任何一个设备的广播风暴都会扩散到全网。 互联网的规模决定了这条线不能松。 第二行是数学问题:一个广播包进路由器,要从每个口各发一份,N 个口就是 N 份。 层级一多,包的数量指数级上涨。 所以「广播域」是被刻意限制的:一个 VLAN 就是一个广播域,路由器是广播域的边界。 这也解释了为什么大网络要划 VLAN——不划,广播就互相干扰。
四、动手 Demo:单播、广播、组播,三种发法
列出三类地址,再真发一个组播包看本机能不能自己收到。 「自发自收」能成,正好说明组播是订阅语义。
import socket, struct
GROUP, PORT = '239.255.255.250', 3702
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', PORT))
mreq = struct.pack('4s4s', socket.inet_aton(GROUP),
socket.inet_aton('0.0.0.0'))
s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
print('加入组播组 %s:成功' % GROUP)
out = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
out.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1)
out.sendto(b'hello-multicast', (GROUP, PORT))
s.settimeout(2)
data, addr = s.recvfrom(1024)
print('自发自收:收到 %d 字节,来自 %s' % (len(data), addr[0]))
out.close()
s.close()
实跑输出:
--------------------------------------------------------------------
单播、广播、组播,三种发法
--------------------------------------------------------------------
**按接收端数量分,只有三类:一对一(单播)、一对全(广播)、一对多(组播)。**
类型 发给谁 典型地址 用在什么场合
---------------------------------------------------------------------
单播 一个确定地址 `192.168.3.1` **绝大多数通信**
广播 本网段全段 `192.168.3.255` **ARP、DHCP**
组播 一群订阅者 `239.255.255.250` **视频流、设备发现**
下面真发一个组播包,看本机能不能自己收到:
加入组播组 239.255.255.250:成功
自发自收:收到 15 字节
**「自己发、自己收」这件事能成,说明组播的语义是「订阅」而不是「投递」**:
**发送方不知道有谁在听,接收方主动加入组。**
**广播和组播的关键区别**:
**广播只有同一网段能收到,路由器默认不转发;组播可以跨网段,但要路由器支持 IGMP。**
**所以「设备发现」这类功能常用组播而不是广播**——**它至少有机会跨过一个路由器。**
收到 15 字节,正好是 hello-multicast 的长度:
本机发出的包,被本机自己加入的那个组收到了。
这件事能成,说明组播的语义是「订阅」而不是「投递」——
发送方不知道有谁在听,接收方主动加入组。
IP_MULTICAST_TTL 设成 1 表示「只在本网段」:每过一个路由器减 1,减到 0 就丢。
这和单播的 TTL 是同一个机制,只是组播默认值更小。
bind(('', PORT)) 里的空字符串表示「所有网卡都听」:
如果写成 127.0.0.1,就收不到组播包了——因为组播包是从真实网卡进来的。
这个细节是初学者最常踩的坑。
30 秒实验
- 把
IP_MULTICAST_TTL改成 0,看还能不能收到——0 表示不出本机。 - 把
GROUP改成224.0.0.1(所有主机组),再跑一遍。 - 算一下:192.168.3.152/24 的广播地址是多少?用「网络部分不变、主机部分全 1」自己算一遍。
常见误区
- 以为组播是「一对多投递」。 它是订阅:发送方不知道有谁在听,接收方主动加入组。
- 以为广播能跨路由器。 路由器默认不转发广播,广播域止于路由器——这是为了保护网络不被泛洪。
- 以为组播和广播一样出不了本网段。 组播可以跨网段,前提是路由器支持 IGMP 并维护成员表。
自测三问(点开对答案)
1. 单播、广播、组播分别发给谁? —— **单播给一个确定地址,广播给本网段全段,组播给一群订阅者。**互联网流量的绝大多数是单播。 2. 为什么「自发自收」能证明组播是订阅语义? —— **因为本机加入了那个组,自己发的包自己也能收到。**发送方不需要知道接收方是谁,只往组地址发。 3. 路由器为什么不转发广播? —— **怕泛洪**:广播本质是「发给所有人」,一旦跨网段,一个设备的广播风暴会扩散到全网, 且每个口都要复制一份。下一节进入 1.8:地址——地址为什么必须唯一,又为什么必须分层。
- ☐ (本节完,下一节 1.8.1 地址的唯一性)