Networking

CoAP — Constrained Application Protocol

An application-layer protocol (RFC 7252) built on top of UDP, designed for small, battery-powered, memory-constrained devices talking over lossy networks. It borrows HTTP's REST model — resources, URIs, GET/POST/PUT/DELETE — but re-encodes everything into a compact binary format and adds its own lightweight reliability on top of UDP's unreliable delivery.

Quick reference
What it is
A REST-style protocol (GET/POST/PUT/DELETE) over UDP, purpose-built for constrained devices.
Mental model
HTTP's request/response idea, re-encoded into a tiny binary format and given just enough optional reliability, not TCP's full guarantee.
Key numbers
4-byte fixed header (vs. HTTP's 100+ bytes of text); CON messages retry until ACKed or give up.
Reach for it when
A constrained device needs REST-style request/response but can't afford TCP's connection state or handshake cost.

Why not just use HTTP?

HTTP over TCP works fine on constrained devices in principle, but three things make it a poor fit for the smallest nodes: TCP's connection setup and header overhead per request, HTTP's verbose text-based headers, and TCP's requirement to hold open connection state per peer — expensive in RAM on a device that might have only a few KB to spare. CoAP keeps the REST *model* everyone already knows, but drops the transport and encoding that made it heavy.

Every CoAP message starts with a fixed 4-byte binary header — contrast with an HTTP request line plus headers, which is easily 100+ bytes of text. Laid out byte by byte, a full message looks like this:

CoAP message layout: 4-byte fixed header followed by a variable-length token, options, and payload fixed 4-byte header variable length Ver·T·TKL 1 byte Code 1 byte Message ID 2 bytes Token 0–8 bytes Options TLV, variable 0xFF Payload optional
Solid boxes are always exactly that size; dashed boxes are variable-length. The 0xFF marker byte is what tells a parser "options end here, payload starts."
FieldSizePurpose
Ver2 bitsProtocol version.
Type2 bitsCON, NON, ACK, or RST (see below).
TKL4 bitsLength of the token that follows (used to match responses to requests).
Code8 bitsMethod (e.g. GET) on a request, status (e.g. 2.05 Content) on a response.
Message ID16 bitsDetects duplicates and pairs ACKs with the message they acknowledge.

After the header: an optional token, a set of TLV-encoded options (the CoAP equivalent of HTTP headers — URI-Path, Content-Format, Observe, etc.), and an optional payload.

CoAP over UDP: how a packet is actually sent

CoAP has no transport of its own — no connection, no streaming, no chunking beyond what Block-wise transfer (further down this page) explicitly implements. Every CoAP message — the entire 4-byte header plus token, options, and payload laid out above — is sent as the whole payload of exactly one UDP datagram, nothing more and nothing less. There's no "opening a CoAP connection" anywhere in this picture — a client just builds those bytes in memory and hands them to the operating system's ordinary UDP socket API, the same one any other UDP program would use.

A complete CoAP message — header, token, options, and payload — riding unchanged as the entire payload of one UDP datagram, itself inside an IP packet inside an Ethernet frame Ethernet frame — MAC → MAC, local network only IP packet — IP → IP, across networks UDP datagram — port → port, destination 5683 CoAP message header + token + options + payload 8 B UDP header ↑ 20 B IP header ↑ 14 B Ethernet header
Same nesting as Ethernet's general encapsulation diagram — this page just zooms in on what fills the innermost box: an entire CoAP message, byte for byte identical to the layout in Message format above.

Step by step, sending one CoAP request looks like this:

  1. The client builds the entire CoAP message in memory — header, token, options, and payload — exactly as laid out in Message format above.
  2. It makes one ordinary UDP send call (conceptually sendto(socket, coap_bytes, server_ip, 5683)) — no handshake, no connect step, no "opening" anything first.
  3. The OS's UDP layer prepends its own 8-byte header (source port, destination port 5683, length, checksum) and hands the result down to IP.
  4. IP prepends a 20-byte header (source/destination address, protocol number 17 for "this is UDP") and hands it to the link layer.
  5. Ethernet or Wi-Fi wraps that in a frame and puts it on the wire — the CoAP bytes themselves never change from this point until they're unwrapped at the other end.
  6. On arrival, the server's network stack strips Ethernet's header, then IP's, then UDP's — in reverse order — and delivers exactly the original CoAP bytes to whichever socket is bound to port 5683.
  7. The server reads the datagram in one call — UDP is message-oriented, so one recvfrom() returns one complete message, never a partial fragment of it — and parses the first byte as Ver/T/TKL, same as Message format described.
  8. If the request was CON, the server builds a reply CoAP message and sends it back the exact same way: a brand-new, independent UDP datagram addressed to the client's address and port — not a "reply" riding some existing connection, because there isn't one.
There is no connection, anywhere in this picture

Every request and every response is its own standalone UDP datagram — the OS doesn't remember anything about a CoAP "session" between them. That's exactly why CoAP needs its own Message ID and Token fields (next section) to match a response back to the request that caused it. TCP gets that matching for free from its connection state; CoAP has to rebuild a thin version of it at the application layer instead, because UDP hands it nothing to work with.

In pure packet count, this is the entire cost of one CoAP exchange: one UDP datagram out, one back — zero back for a NON message. Compare that to HTTP over a freshly-opened TCP connection — SYN, SYN-ACK, ACK, then the HTTP request, then the response, then a FIN/FIN-ACK teardown — six or more packets before either side is even done, which is exactly the overhead Why not just use HTTP? above was pointing at.

Reliability on top of UDP

Raw UDP (see UDP) gives you no delivery guarantee at all. CoAP adds a thin, optional reliability layer using its message type field, without pulling in anything like TCP's connection state:

CON
Confirmable — the sender retransmits (with exponential backoff) until an ACK arrives or retries are exhausted.
NON
Non-confirmable — fire-and-forget, no retransmission; used for data where a missed update doesn't matter (e.g. periodic sensor readings).
ACK
Acknowledges a CON message, and — since CoAP is request/response — usually piggybacks the actual response in the same packet.
RST
Sent when a message can't be processed (e.g. the receiver has no state for it) — tells the sender to stop.

This is deliberately much simpler than TCP's sliding windows and congestion control — CoAP only needs "did the other end get this request/response," not a general-purpose reliable byte stream.

Message exchanges, frame by frame

The tables above describe fields; these diagrams show what actually goes out on the wire and what comes back, for the three exchanges that come up constantly. CON, ACK, and NON below are colored consistently with the message-type field they represent.

A confirmable request with its response piggybacked on the acknowledgement Client Server CON GET /temperature MID=0x21 · Token=0x9c ACK 2.05 Content "23.5°C" MID=0x21 (response piggybacked on the ACK)
Confirmable request → the response rides back on the same ACK packet. One round trip, one guarantee of delivery.
A non-confirmable message sent without expecting any reply Client Server NON PUT /led "on" MID=0x45 fire-and-forget — no ACK is expected or sent
Used when a missed message just isn't worth a retry — e.g. one of many periodic sensor readings.
A confirmable message lost in transit, retransmitted after a timeout, then acknowledged Client Server CON GET /door MID=0x30 — lost in transit ACK_TIMEOUT elapses with no ACK → client retransmits CON GET /door (retransmit) MID=0x30 — same Message ID, so the server can dedupe ACK 2.05 Content "unlocked" MID=0x30
This is the entire reliability model: retry with backoff on a fixed Message ID, until an ACK arrives or retries run out.

REST semantics: methods and response codes

CoAP requests target a resource identified by a URI path, using the same method vocabulary as HTTP: GET, POST, PUT, DELETE. Response codes reuse HTTP's class structure but pack the class and detail into a single byte, written as class.detail:

CoAP codeMeaningHTTP equivalent
2.05 ContentSuccess, response has a body200 OK
2.01 CreatedResource created201 Created
4.04 Not FoundNo such resource404 Not Found
4.05 Method Not AllowedMethod doesn't apply to this resource405 Method Not Allowed
5.00 Internal Server ErrorServer-side failure500 Internal Server Error

Observe and block-wise transfer

Two extensions matter enough in practice to know by name:

Observe: one registration, then multiple pushed notifications without the client re-requesting Client Server CON GET /temperature, Observe=0 MID=0x50 — "register me for updates" ACK 2.05 Content 23.5°C, Observe=1 MID=0x50 — first value, piggybacked as usual … time passes, the resource's value changes … CON 2.05 Content 24.1°C, Observe=2 MID=0x51 — server-initiated push, no new request needed ACK — MID=0x51 …repeats on every future change until the client cancels…
One GET with Observe=0 turns into an ongoing subscription — the server pushes, the client just acknowledges.

CoAP vs. raw UDP vs. MQTT vs. HTTP

Raw UDPCoAPMQTTHTTP
TransportUDPUDP (usually)TCPTCP
ModelYou define everythingREST (request/response)Publish/subscribe via brokerREST (request/response)
ReliabilityNone built-inOptional, per-message (CON/ACK)Broker-mediated QoS levelsTCP's (always reliable)
Header overhead8 bytes4-byte fixed header + compact options2-byte fixed header + variable~100s of bytes, text
Connection stateNoneNone requiredPersistent TCP connection per clientPer-connection (or per-request)
Multicast supportYes (UDP)Yes — a defined use caseNo (TCP is point-to-point)No
Typical useCustom minimal protocols, telemetryConstrained-device REST APIs, device controlMany-to-many sensor/event fan-outGeneral web APIs, non-constrained clients

When to choose what

Decision guide

Pros and cons

ProsCons
Raw UDP Minimal possible overhead; total control; simplest mental model for a fixed, closed system. No standard semantics — you invent framing, addressing, and reliability yourself, and re-invent bugs others already fixed; zero interoperability.
CoAP Small, fast, low-power; REST semantics developers already know; optional reliability; native multicast/discovery; DTLS (CoAPs) or OSCORE for security. Still UDP underneath, so NAT traversal and firewall friendliness are worse than TCP-based options; smaller ecosystem/tooling than HTTP; security is an added layer, not built in by default.

Real-world applications

Gotchas

Field notes
What I learned

CoAP's real trick isn't "REST over UDP" by itself — plenty of things run over UDP. It's putting just enough of TCP's useful behavior (optional reliability, dedup, request/response correlation) back on top of UDP as an application-layer concern, so a constrained device only pays for the guarantees it actually needs on a per-message basis, instead of a connection-wide all-or-nothing guarantee like TCP gives you.