# UDP - User Datagram Protocol _Сеть: L4 и выше · LinuxLab Knowledge Base_ **TL;DR:** UDP - простой протокол доставки датаграмм без установки соединения, без ретрансмитов, без гарантии порядка. Заголовок 8 байт. Применение: DNS, DHCP, QUIC, VoIP, любой случай когда задержка важнее надёжности. ## Что делает UDP [port](/kb/port.md) плюс контрольная сумма - вот и весь UDP. В отличие от [[tcp-handshake|TCP]], UDP не открывает соединение, не нумерует пакеты, не подтверждает доставку. Просто берёт твои данные, оборачивает в datagram с src/dst-портами и кидает в [[ipv4-addressing|IP]]-сеть. ## Заголовок UDP - 8 байт ``` 0 7 8 15 16 23 24 31 +--------+--------+--------+--------+ | src port | dst port | +--------+--------+--------+--------+ | length | checksum | +--------+--------+--------+--------+ | data ... | ``` - **src/dst port** - 16 бит, 0-65535 (как у TCP) - **length** - длина UDP-заголовка + payload в байтах - **checksum** - 16-битная сумма заголовка + payload (опциональна в IPv4, обязательна в IPv6) Сравни с TCP, где заголовок минимум 20 байт + опции - UDP легче в 2.5 раза. ## Когда выбирать UDP | Сценарий | Почему UDP | |----------|------------| | DNS-запрос (один query, один ответ) | TCP-handshake = 1.5×RTT накладных, не нужен | | DHCP (broadcast, новый клиент) | TCP не работает по broadcast | | VoIP / видеозвонки | потеря 1-2 пакета < перезапрос; ретрансмит вреден | | NTP (sync времени) | ретрансмит ломает точность | | QUIC (HTTP/3) | контроль доставки в user-space, не в ядре | | Стриминг (RTP) | потерю кадра проще пропустить | | Игры (real-time) | актуальное состояние важнее старого | ## Когда **нельзя** UDP - Большие данные где порядок важен (HTTP/1-2, SSH, базы) - нужен TCP - Reliability на уровне приложения - либо ты пишешь свой ack/retry поверх UDP (как QUIC), либо не используй UDP ## Размер пакета и фрагментация Теоретический максимум UDP-payload = 65 507 байт (65535 - IP-заголовок 20 - UDP-заголовок 8). Но **MTU Ethernet = 1500**, и UDP > MTU → IP-фрагментация на уровне сети. Это плохо: - Если хоть один фрагмент потерялся - весь datagram дропается - Многие firewall'ы дропают фрагменты по дефолту - На пути MTU может быть меньше 1500 (тоннели, VPN) Поэтому в UDP-приложениях держат payload **под 1472 байта** (1500 - IP - UDP). DNS исторически ограничен 512 байтами в UDP, для больших ответов переключается на TCP (или EDNS0 расширяет до 4096). ## UDP-сокет на Linux ```python import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP = SOCK_DGRAM s.bind(("0.0.0.0", 5353)) data, addr = s.recvfrom(4096) # blocking read одного датаграма s.sendto(b"pong", addr) # ответ конкретному клиенту ``` Никаких `listen()`/`accept()`/`connect()` - сразу `bind()` и `recvfrom()`. ## Что видно в tcpdump ``` IP 10.0.0.1.55321 > 8.8.8.8.53: UDP, length 32 IP 8.8.8.8.53 > 10.0.0.1.55321: UDP, length 64 ``` Никаких флагов SYN/ACK - просто два независимых датаграма. По одному пакету tcpdump не скажет, связаны они или нет (нет state). ## Заметки про conntrack Хотя у UDP нет state, [[conntrack|netfilter conntrack]] всё равно создаёт «псевдо-соединение» по 5-tuple (src-ip, src-port, dst-ip, dst-port, proto=UDP) и держит его 30 секунд после последнего пакета. Это нужно чтобы [[nat|NAT]] работал и чтобы ответный пакет проходил `RELATED,ESTABLISHED`. ## Когда что-то пошло не так - **No response** - UDP не сообщит об ошибке. Если порт закрыт - ядро может прислать ICMP unreachable, но это best-effort - **Out-of-order delivery** - UDP не сортирует. Приложение должно само разбираться через свой sequence-number - **Duplicate packets** - могут быть. Опять, приложение само - **Большой пакет → TIMEOUT** - вероятно фрагмент потерялся; шли меньше ## Команды ```bash ss -unlp ``` Все UDP-сокеты в LISTEN: -u UDP, -n числа, -l listen, -p процессы ```bash tcpdump -i any -nn 'udp port 53' -c 5 ``` Первые 5 DNS-запросов/ответов - чистый UDP ```bash nc -u 8.8.8.8 53 ``` Открыть UDP-сокет в netcat - можно попробовать вручную послать байты ```bash echo 'ping' | nc -u -w1 1.2.3.4 5353 ``` Послать UDP-датаграмм и подождать секунду ответ (-w1) ```bash iperf3 -u -c host -b 100M -t 10 ``` Замерить UDP-throughput с генерацией 100Mbit/s в течение 10с ## См. также - [TCP three-way handshake](/kb/tcp-handshake.md) - [Порт - как несколько сервисов делят один IP](/kb/port.md) - [CoAP - REST для constrained-устройств поверх UDP](/kb/coap.md) - [Conntrack - память Linux о всех сетевых соединениях](/kb/conntrack.md) - [dig - DNS-разведка с подробностями](/kb/cmd-dig.md) - [iperf3 - измерение bandwidth](/kb/cmd-iperf3.md) - [QUIC и HTTP/3 - современный транспорт поверх UDP](/kb/quic-http3.md)