Четыре способа доставки пакета
IP-адресация определяет, кому пакет доставляется. Исторически различают четыре модели:
Unicast — один отправитель, один получатель (1 → 1)
Broadcast — один отправитель, все в сегменте LAN (1 → all)
Multicast — один отправитель, группа подписчиков (1 → many)
Anycast — один отправитель, ближайший из группы (1 → nearest)
Unicast: Broadcast:
A ──────────► B A ──┬──► B
├──► C
├──► D
└──► E (все в LAN)
Multicast: Anycast:
A ──┬──► B(sub) A ───► B (nearest)
├──► C(sub) ╲╱
╳ → D(not sub) ╱╲ (B, C, D share IP)
└──► E(sub)
Unicast
Самый привычный тип -- пакет адресован конкретному IP. 99% трафика интернета -- unicast.
Ethernet header: dst MAC = aa:bb:cc:dd:ee:ff (конкретный хост)
IP header: dst IP = 192.168.1.5
Коммутаторы и маршрутизаторы доставляют пакет одному получателю. Если нужно передать одно и то же 1000 клиентам -- 1000 unicast-отправлений. Неэффективно, но просто.
Broadcast
Пакет адресован всем в LAN-сегменте. Существует только в IPv4, на уровне канала (Ethernet) и сети (IP).
Ethernet: dst MAC = ff:ff:ff:ff:ff:ff ← broadcast MAC
IPv4:
Limited broadcast: 255.255.255.255 ← никогда не роутится
Directed broadcast: 192.168.1.255 ← broadcast для подсети
Коммутатор получив broadcast MAC, копирует фрейм на все порты. Роутеры по умолчанию дропают broadcast (иначе был бы storm).
Где используется broadcast
ARP (Address Resolution Protocol)
Хост хочет послать пакет на 192.168.1.5, но не знает его MAC:
ARP request (broadcast):
"Who has 192.168.1.5? Tell 192.168.1.10"
Sent to ff:ff:ff:ff:ff:ff
Все в LAN получают фрейм.
Хост с IP 192.168.1.5 отвечает:
"192.168.1.5 is at aa:bb:cc:dd:ee:ff"
ARP reply -- unicast обратно.
# Посмотреть ARP таблицу
ip neigh show
# 192.168.1.1 dev eth0 lladdr 00:11:22:33:44:55 REACHABLE
# 192.168.1.5 dev eth0 lladdr aa:bb:cc:dd:ee:ff STALE
# Вручную отправить ARP request
arping -I eth0 192.168.1.5
DHCP Discovery
Клиент только включился, не знает ни своего IP, ни адреса DHCP-сервера:
1. Client → broadcast: DHCPDISCOVER (src=0.0.0.0, dst=255.255.255.255)
2. Server → broadcast: DHCPOFFER (предлагает IP, параметры)
3. Client → broadcast: DHCPREQUEST (принимаю этот offer)
4. Server → broadcast: DHCPACK (confirmed)
Всё через broadcast, потому что на этапе 1-3 у клиента ещё нет IP для unicast.
Wake-on-LAN
Magic packet на broadcast MAC пробуждает спящий хост.
Минусы broadcast
- Шум в LAN -- каждый хост обязан обработать broadcast (хотя бы на уровне сетевой карты).
- Ломает сегментацию -- broadcast domain = размер VLAN.
- Storm потенциал -- петля в топологии заставляет broadcast крутиться бесконечно, съедая всю пропускную способность.
IPv6 полностью отказался от broadcast -- его заменил multicast с well-known адресами (например, ff02::1 -- все ноды в линке).
Multicast
Multicast -- между unicast и broadcast: пакет доставляется группе заинтересованных. Экономит bandwidth по сравнению с N unicast-копиями.
Адресация
- IPv4:
224.0.0.0/4(класс D,224.0.0.0-239.255.255.255).224.0.0.0/24-- link-local (не роутятся).239.0.0.0/8-- administratively scoped (частный multicast).
- IPv6:
ff00::/8. С префиксами scope:ff02::-- link,ff05::-- site,ff0e::-- global.
Известные адреса:
224.0.0.1-- все хосты в линке.224.0.0.2-- все роутеры.224.0.0.251-- mDNS (Bonjour).239.255.255.250-- SSDP (UPnP discovery).ff02::1-- all nodes (IPv6 link-local).ff02::fb-- mDNS (IPv6).
IGMP: подписка на группу
Host подписывается на multicast-группу через IGMP (Internet Group Management Protocol, IPv4) или MLD (Multicast Listener Discovery, IPv6):
Host → IGMP Membership Report → "я хочу получать 239.1.1.1"
Switch с IGMP snooping видит запрос, запоминает: "порт 5 = 239.1.1.1".
Теперь multicast-трафик на 239.1.1.1 копируется только на порт 5,
а не на все порты, как было бы без snooping.
Router запоминает: "через интерфейс eth0 есть подписчики 239.1.1.1".
Через PIM (Protocol Independent Multicast) строит дерево доставки
между роутерами.
IGMP v2: join, leave, query. IGMP v3: source-specific multicast (SSM) -- "только от этого отправителя".
Use cases multicast
Video streaming в LAN (IPTV)
Один сервер шлёт 4K поток на 239.0.1.1. 1000 set-top-box'ов подписаны, каждый получает тот же поток, сеть несёт его один раз через основные линки.
1 Gbps stream, 1000 subscribers:
Unicast: 1000 × 1 Gbps = 1 Tbps (невозможно)
Multicast: 1 × 1 Gbps = 1 Gbps (идеально)
Stock ticker
NASDAQ/NYSE раздают market data по multicast. Trading firms подписываются, latency минимальна, нагрузка на source константная.
Service discovery (mDNS/Bonjour)
Когда iPhone печатает на принтере Brother без настройки DNS -- это mDNS. Запросы идут на 224.0.0.251:5353, все mDNS-клиенты в LAN слушают и отвечают.
# Посмотреть mDNS-устройства в сети
avahi-browse -a
# + eth0 IPv4 HP LaserJet Pro M15w _ipp._tcp local
# + eth0 IPv4 Apple TV _airplay._tcp local
# + eth0 IPv4 Home Assistant _home-assistant._tcp local
# Resolve .local имя
avahi-resolve -n my-printer.local
# my-printer.local 192.168.1.77
Cluster discovery
Kafka, Cassandra, Consul могут использовать multicast для bootstrap discovery внутри DC (хотя cloud-native предпочитают явные seed lists, потому что AWS/GCP режут multicast).
Router protocols
OSPF использует 224.0.0.5 (all OSPF), 224.0.0.6 (DR/BDR). RIPv2 -- 224.0.0.9. Внутренняя магия интернета работает на multicast.
Проблемы multicast в проде
- Не работает в публичном интернете -- провайдеры не распространяют multicast между AS. Только в пределах LAN или управляемых VPN.
- AWS/GCP VPC не поддерживают IP multicast напрямую (AWS есть Transit Gateway multicast, но ограниченно).
- IGMP snooping обязателен -- без него коммутатор флудит multicast на все порты (превращается в broadcast).
- Сложная отладка -- нужны специализированные инструменты (
mtrace,smcroute).
Anycast
Тот же IP анонсирован из многих мест, ближайший (по BGP) обрабатывает запрос. Детально разбирали в главе про BGP.
Краткое напоминание:
- Работает на IP-уровне, через routing protocol (BGP).
- Не требует специальной поддержки клиента -- обычный IP.
- Идеален для stateless протоколов (DNS over UDP, HTTP/QUIC).
- Stateful TCP может мигрировать между POP -- проблема.
IPv6 отказался от broadcast
В IPv4 broadcast использовался для:
- ARP -- найти MAC по IP.
- DHCP discovery.
- Router discovery.
В IPv6 эти задачи решены через multicast с well-known адресами:
| IPv4 broadcast task | IPv6 multicast equivalent |
|---|---|
| ARP | Neighbor Discovery (NDP) на ff02::1:ffxx:xxxx (solicited-node) |
| DHCP | DHCPv6 на ff02::1:2 (all DHCP servers) |
| Router advertisement | ff02::2 (all routers) |
| All hosts | ff02::1 (all nodes) |
Преимущество: пакет приходит только тем, кому нужно (не все хосты обязаны обрабатывать).
Сравнение всех четырёх
| Свойство | Unicast | Broadcast | Multicast | Anycast |
|---|---|---|---|---|
| Получатели | 1 | Все в LAN | Подписчики группы | Ближайший из группы |
| Scope | Global | LAN only | LAN или internet (PIM) | Global via BGP |
| IP range (v4) | Normal | 255.255.255.255, x.x.x.255 |
224.0.0.0/4 |
Normal (anycast через routing) |
| IP range (v6) | Normal | нет | ff00::/8 |
Normal |
| Требует подписки | Нет | Нет | Да (IGMP) | Нет |
| Bandwidth efficiency | Низкая для N клиентов | Высокая в LAN | Высокая, распределена | Высокая (клиент идёт в ближайший) |
| Router behavior | Forward | Drop (обычно) | Build distribution tree (PIM) | Normal unicast forwarding |
| Использование | HTTP, SSH, DB | ARP, DHCP, WoL | IPTV, stock, mDNS | DNS, CDN |
Практика: UDP multicast в Go
Multicast на UDP: отправитель шлёт на группу, получатели слушают группу. Никакого handshake, никаких гарантий.
Receiver (подписчик)
package main
import (
"fmt"
"log"
"net"
"time"
)
// MulticastReceiver joins a group and reads datagrams.
type MulticastReceiver struct {
group *net.UDPAddr
conn *net.UDPConn
bufLen int
}
// NewMulticastReceiver creates a listener for the given group and port.
// The interface is chosen by OS unless explicitly set via ListenMulticastUDP.
func NewMulticastReceiver(groupAddr string) (*MulticastReceiver, error) {
addr, err := net.ResolveUDPAddr("udp4", groupAddr)
if err != nil {
return nil, fmt.Errorf("resolve: %w", err)
}
// Bind on all interfaces for the group
conn, err := net.ListenMulticastUDP("udp4", nil, addr)
if err != nil {
return nil, fmt.Errorf("listen: %w", err)
}
// Larger read buffer helps with bursts
if err := conn.SetReadBuffer(1 << 20); err != nil {
log.Printf("SetReadBuffer: %v", err)
}
return &MulticastReceiver{
group: addr,
conn: conn,
bufLen: 2048,
}, nil
}
// Receive loops and calls handler for each datagram until ctx is cancelled.
func (r *MulticastReceiver) Receive(handler func(src *net.UDPAddr, data []byte)) error {
buf := make([]byte, r.bufLen)
for {
_ = r.conn.SetReadDeadline(time.Now().Add(5 * time.Second))
n, src, err := r.conn.ReadFromUDP(buf)
if err != nil {
// Timeout is expected, continue
if ne, ok := err.(net.Error); ok && ne.Timeout() {
continue
}
return fmt.Errorf("read: %w", err)
}
// Copy since buf will be reused
data := make([]byte, n)
copy(data, buf[:n])
handler(src, data)
}
}
func (r *MulticastReceiver) Close() error {
return r.conn.Close()
}
func main() {
// 239.1.1.1 is administratively-scoped private multicast
rec, err := NewMulticastReceiver("239.1.1.1:9999")
if err != nil {
log.Fatal(err)
}
defer rec.Close()
log.Println("Listening on 239.1.1.1:9999")
if err := rec.Receive(func(src *net.UDPAddr, data []byte) {
log.Printf("from %s: %s", src, string(data))
}); err != nil {
log.Fatal(err)
}
}
Sender (издатель)
package main
import (
"fmt"
"log"
"net"
"time"
)
// MulticastSender broadcasts datagrams to a group.
type MulticastSender struct {
conn *net.UDPConn
target *net.UDPAddr
}
// NewMulticastSender opens a UDP connection to the group.
// TTL controls how many router hops the packet can cross.
func NewMulticastSender(groupAddr string, ttl int) (*MulticastSender, error) {
addr, err := net.ResolveUDPAddr("udp4", groupAddr)
if err != nil {
return nil, fmt.Errorf("resolve: %w", err)
}
conn, err := net.DialUDP("udp4", nil, addr)
if err != nil {
return nil, fmt.Errorf("dial: %w", err)
}
// Default multicast TTL is 1 (link-local only).
// For cross-router multicast set explicit TTL.
// Requires golang.org/x/net/ipv4 for portable IP_MULTICAST_TTL.
// Here we use raw syscall hint via connection -- see ipv4.NewPacketConn for full control.
return &MulticastSender{
conn: conn,
target: addr,
}, nil
}
func (s *MulticastSender) Send(data []byte) error {
_, err := s.conn.Write(data)
return err
}
func (s *MulticastSender) Close() error {
return s.conn.Close()
}
func main() {
sender, err := NewMulticastSender("239.1.1.1:9999", 1)
if err != nil {
log.Fatal(err)
}
defer sender.Close()
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
counter := 0
for range ticker.C {
msg := fmt.Sprintf("tick #%d @ %s", counter, time.Now().Format(time.RFC3339))
if err := sender.Send([]byte(msg)); err != nil {
log.Printf("send: %v", err)
continue
}
log.Printf("sent: %s", msg)
counter++
}
}
Запуск и проверка
# Терминал 1 (receiver)
go run receiver.go
# 2026/04/18 10:00:01 Listening on 239.1.1.1:9999
# Терминал 2 (sender)
go run sender.go
# 2026/04/18 10:00:02 sent: tick #0 @ ...
# Терминал 3 (второй receiver -- оба получают тот же поток)
go run receiver.go
# Посмотреть multicast-трафик tcpdump'ом
sudo tcpdump -i any -nn 'host 239.1.1.1'
# 10:00:02 IP 192.168.1.10.51234 > 239.1.1.1.9999: UDP, length 42
Нюансы multicast в Linux
# Проверить, включён ли multicast forwarding
sysctl net.ipv4.conf.all.mc_forwarding
# net.ipv4.conf.all.mc_forwarding = 0
# Посмотреть multicast-группы, в которых состоит интерфейс
ip maddr show dev eth0
# 1: eth0
# inet 224.0.0.1 (all hosts)
# inet 224.0.0.251 (mDNS, если avahi бежит)
# Посмотреть IGMP-трафик
sudo tcpdump -i eth0 -nn 'igmp'
# Отладка multicast routing (требует smcrouted)
ip mroute show
Выводы
- Четыре модели доставки: unicast (1→1), broadcast (1→all в LAN), multicast (1→группа подписчиков), anycast (1→ближайший).
- Broadcast только в IPv4 и только в LAN (ARP, DHCP). IPv6 заменил его link-local multicast.
- Multicast экономит bandwidth для 1-ко-многим: один поток IPTV на 1000 клиентов вместо 1000 unicast-копий.
- IGMP (v4) / MLD (v6) -- протоколы подписки на группу; IGMP snooping на коммутаторах обязателен, иначе multicast = broadcast.
- Private multicast:
239.0.0.0/8для site-локальных задач,224.0.0.0/24для link-local control (ARP-аналоги, OSPF). - mDNS (
224.0.0.251:5353) -- zero-configuration service discovery в LAN (.localимена). - В публичном интернете multicast не работает -- провайдеры не форвардят между AS.
- Anycast -- routing-based альтернатива: один IP, много POP, BGP выбирает ближайший.
- Go
net.ListenMulticastUDP+net.DialUDPпокрывают 95% multicast-сценариев без внешних библиотек.