Зачем CoAP
Классический HTTP не работает на сильно constrained устройствах:
- Маленькая микроконтроллерная RAM (десятки KB)
- Battery-powered (нельзя держать TCP-сессии)
- Lossy radio (LoRa, Zigbee, Thread mesh)
- Не во всех есть IPv4-stack полностью
HTTP-парсер - сложный, headers большие (даже минимальный GET ~80 байт).
CoAP (Constrained Application Protocol, RFC 7252, 2014) - REST-like protocol специально для них. Поверх UDP, минимальный header, REST-семантика. Сделан так, чтобы device мог быть и client, и server одновременно.
Применения:
- LwM2M (Lightweight M2M от OMA) - device management поверх CoAP
- Thread (mesh-сеть для smart home) - CoAP в native protocol stack
- ZigBee Pro - CoAP опция
- OMA Lightweight Object Tree - сенсоры/actuators в LwM2M
- Energy management в HEMS
CoAP vs HTTP - семантика
| HTTP | CoAP |
|---|---|
| TCP | UDP (TCP-вариант RFC 8323) |
| Text headers | Bitfields, options |
| Headers ~80+ байт | Header ~4 байта |
| Methods GET/POST/... | Те же 4 + блок-расширения |
| URLs | URIs, Uri-Path/Uri-Query options |
| Response codes 200/404/... | Кодировано по 5.x.y (5 классов × 32) |
| Stateless request/response | Confirmable / Non-confirmable |
| EventSource/SSE/WebSocket для push | Observe-pattern native |
Идея: один и тот же ментальный модель (URL, метод, status), но ужатый под радио и мелкое железо.
Wire format
Минимальный CoAP-header - 4 байта:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver| T | TKL | Code | Message ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Token (if any, TKL bytes) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options (if any) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1 1 1 1 1 1 1 1| Payload (if any) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- Ver - версия (1)
- T - тип сообщения: CON (confirmable), NON (non-conf), ACK, RST
- TKL - длина token (0-8 байт)
- Code - 8 бит, формат
c.dd(3 бит class + 5 бит detail). Method: 0.01=GET, 0.02=POST, 0.03=PUT, 0.04=DELETE Response: 2.05=Content (≈200), 4.04=Not Found (≈404), 5.00=Internal Error - Message ID - 16-битный, для duplicate detection
- Token - 0-8 байт, для request-response correlation (отличается от Message ID - один request может породить много responses через observe)
- Options - URI path, content-format, ETag, Observe, ...
- 0xFF - разделитель payload
Минимальный GET с пустым payload - 4 байта + URI options.
Confirmable vs Non-confirmable
CoAP - поверх UDP, нет встроенной reliability. Решение - тип сообщения:
CON (Confirmable) - reliable
client ──CON GET /sensor (mid=42)──► server
client ◄──ACK 2.05 Content (mid=42)── server
Ack обязателен. Без ack - retry (exp backoff): 2s, 4s, 8s, 16s, 32s
- максимум 5 попыток (RFC 7252).
Если piggyback - сервер шлёт ACK с уже готовым response (одна посылка).
NON (Non-confirmable) - fire-and-forget
client ──NON GET /sensor (mid=42)──► server
Никакого ack. Если потерян - потерян. Для high-rate телеметрии где одно потеряное сообщение неважно.
Похоже на mqtt QoS 0 (NON) и QoS 1 (CON).
Methods и status codes
Methods (RFC 7252):
- GET 0.01 - получить ресурс
- POST 0.02 - создать
- PUT 0.03 - обновить
- DELETE 0.04 - удалить
- расширения:
- FETCH 0.05 (RFC 8132) - GET с body (для сложных queries)
- PATCH 0.06, iPATCH 0.07 (RFC 8132)
Response classes:
- 2.xx - Success: 2.01 Created, 2.02 Deleted, 2.03 Valid (ETag), 2.04 Changed, 2.05 Content
- 4.xx - Client error: 4.00 Bad Request, 4.04 Not Found, 4.05 Method Not Allowed, 4.06 Not Acceptable, 4.13 Request Entity Too Large
- 5.xx - Server error: 5.00 Internal, 5.03 Service Unavailable
URI и options
URI типа coap://gw.example/sensors/temp?units=C. На проводе:
Uri-Host(option 3) = "gw.example"Uri-Path(option 11) = "sensors", "temp" (по сегментам)Uri-Query(option 15) = "units=C"
Опции typed - 28 предопределённых, кастомные через elective/critical flag в опции number.
Каждая option encode'ится с delta-кодированием от предыдущей - компактно.
Observe pattern - server-pushed updates
RFC 7641. Клиент посылает GET с Observe option = 0:
client ──GET /temperature, Observe=0──► server
client ◄──2.05 Content "21.5", Observe=1── server
client ◄──2.05 Content "21.6", Observe=2── server (через 30 сек)
client ◄──2.05 Content "21.7", Observe=3── server
...
Сервер шлёт push'ы каждый раз когда ресурс изменился (или по таймеру). Observe-counter монотонно растёт - помогает обнаружить потерянные/out-of-order responses.
Cancel - GET с Observe = 1 или просто RST в ответ на следующее.
Это CoAP-эквивалент EventSource/WebSocket для push'а - но без открытой сессии (UDP).
Block-wise transfer
Большие payloads через UDP - проблема (фрагментация, потеря). CoAP делит на блоки через Block1/Block2 options (RFC 7959):
Block1 = 0/1/512 означает: блок 0, more=1, размер 512
Клиент шлёт PUT блоками 0..N с Block1, последний с more=0. Сервер собирает.
Размер блока 16-1024 байта. Default - подбор под underlying MTU (обычно 64-256 для LoRa, 512-1024 для WiFi/Thread).
DTLS - security
CoAP сам не шифрует. DTLS (Datagram TLS, RFC 6347) - TLS-over-UDP (как [[tls-handshake|TLS]] но с retransmission и без strict ordering).
CoAP+DTLS = coaps:// на UDP/5684 (vs обычный coap:// на 5683).
Modes:
- NoSec - plain CoAP (для labа)
- PSK (Pre-Shared Key) - shared secret, легковесно для constrained
- RPK (Raw Public Key) - cert без X.509 signature chain
- Certificate - полный X.509 (как HTTPS)
PSK самый популярный в IoT - device shipped с factory key, broker знает.
Современные расширения:
- OSCORE (RFC 8613) - object security, защита payload end-to-end даже через proxy
- EDHOC (RFC 9528) - lightweight key exchange для PSK
CoAP vs MQTT
| Свойство | CoAP | [[mqtt|MQTT]] |
|---|---|---|
| Транспорт | UDP (или TCP/TLS) | TCP/TLS |
| Модель | request-response (REST) | publish-subscribe |
| Broker | не нужен | обязателен |
| Push | observe-pattern | subscribe |
| Min header | 4 байта | 2 байта |
| Reliability | CON ack/retry | QoS 0/1/2 |
| Multicast | да (group communication) | нет |
| Best for | sensor query, RESTful API | telemetry, fan-out |
| Stack | REST-like, знакомый | специфический |
| Security | DTLS | TLS (сложнее на mc) |
В Thread/Matter - CoAP базовый. В classic IoT (industrial, mobile)
- чаще MQTT.
Multicast - group communication
CoAP поддерживает IP multicast: одно request → все devices в multicast-группе отвечают. Для discovery, group control:
GET coap://[ff02::fd]:5683/.well-known/core
──► все CoAP-серверы в локальном link
HTTP такого нет. Полезно в smart home: "все лампы → off".
Только для NON-confirmable (CON через multicast логически невозможен).
Discovery - /.well-known/core
RFC 6690 - стандартный resource directory:
GET /.well-known/core
───►
◄─── 2.05
</sensors/temp>;rt="temperature";if="sensor",
</sensors/humidity>;rt="humidity";if="sensor",
</actuators/led>;rt="light";if="actuator"
Application-level discovery - what resources, what types.
Когда что-то пошло не так
- Запросы пропадают - UDP. Проверь firewall (5683/5684 UDP). Используй CON вместо NON чтобы видеть.
4.13 Request Entity Too Large- payload > MTU. Используй Block1.- DTLS handshake fail - PSK не совпадает или TLS-версия mismatch
(CoAP DTLS обычно DTLS 1.2).
openssl s_client -dtls1_2. - Observe не работает после NAT - NAT-binding истёк, server
шлёт в "никуда". Делай keepalive (CON GET изредка) или сильно
короче
interval. - Sequence number wrap - Observe counter 24-bit, при wrap логика может ошибаться. Большинство реализаций обработали.
- Multicast не доходит - kernel не routing'ит multicast по
дефолту.
ip route add 224.0.0.0/4 dev eth0. И уровень link должен поддерживать (WiFi - часто фильтрует).
Где почитать
- https://datatracker.ietf.org/doc/html/rfc7252 - оригинальный CoAP
- https://coap.technology/ - инструменты, статьи
- https://github.com/eclipse/californium (Java) - reference implementation
- https://github.com/obgm/libcoap (C) - для embedded