NodeJSUDP Sockets (dgram Module)

UDP Sockets (dgram Module)

The dgram module implements UDP — a connectionless, best-effort transport. Unlike TCP, there's no handshake, no connection, no delivery guarantee, and no ordering: you just fire off independent datagrams (packets) and hope they arrive. That sounds worse, but for the right workloads — live video/voice, gaming, telemetry, DNS, service discovery — UDP's low latency and lack of overhead make it the better tool.

UDP vs TCP recap

UDP (dgram)

TCP (net)

Connection

None — send to an address

Handshake first

Delivery

Best-effort (may drop)

Guaranteed (retransmits)

Ordering

None

In-order

Boundaries

Preserved — 1 send = 1 receive

None — byte stream

Latency

Lower

Higher

Broadcast/multicast

Supported

Not possible

UDP preserves message boundaries — TCP's framing problem disappears
A key upside: one `send()` produces exactly one `message` event of the same size on the receiver. You never need the framing logic TCP requires. The trade is that any given datagram may **never arrive**, or arrive **out of order**, or even arrive **twice** — and UDP won't tell you. If you need reliability, you build it on top yourself (or just use TCP).
A UDP server (receiver)

JS
import { createSocket } from 'node:dgram'

const server = createSocket('udp4')   // 'udp4' (IPv4) or 'udp6' (IPv6)

server.on('message', (msg, rinfo) => {
  // msg is a Buffer; rinfo tells you who sent it
  console.log(`got ${msg} from ${rinfo.address}:${rinfo.port}`)
})

server.on('listening', () => {
  const { address, port } = server.address()
  console.log(`listening on ${address}:${port}`)
})

server.bind(5000)   // listen for datagrams on port 5000
There's no connection callback — just `message` events
Because UDP is connectionless, there's no "client connected" notion. The socket simply emits a `message` event for every datagram that arrives, with the payload (`Buffer`) and `rinfo` (sender's address/port/size). To reply, you `send()` back to `rinfo.address`/`rinfo.port`. The same socket can receive from any number of peers.
A UDP client (sender)

JS
import { createSocket } from 'node:dgram'

const client = createSocket('udp4')
const message = Buffer.from('hello over UDP')

client.send(message, 5000, '127.0.0.1', (err) => {
  if (err) console.error(err)
  else console.log('datagram sent')
  client.close()              // no connection to tear down — just release the socket
})
`send` reports it was handed to the OS — NOT that it was delivered
The `send` callback fires once the datagram is queued by the operating system, which is **not** the same as the peer receiving it. UDP gives no delivery confirmation; a successful callback with no error only means "sent to the wire." If the packet is dropped en route, you'll never know. Never treat a clean `send` callback as proof of receipt.
Echo example end-to-end

JS
import { createSocket } from 'node:dgram'

// Server: echo each datagram back to its sender
const server = createSocket('udp4')
server.on('message', (msg, rinfo) => {
  server.send(msg, rinfo.port, rinfo.address)   // reply to the origin
})
server.bind(5000)
Broadcast and multicast — UDP-only powers

UDP can send one datagram to many receivers at once — impossible with TCP. Broadcast hits every host on the local subnet; multicast hits only hosts that joined a group address. These power service discovery (mDNS/Bonjour), streaming, and IoT.

JS
import { createSocket } from 'node:dgram'

const socket = createSocket('udp4')

socket.bind(() => {
  socket.setBroadcast(true)                         // enable broadcast
  socket.send('ping', 5000, '255.255.255.255')      // to the whole subnet

  // Multicast: join a group, then send/receive on it
  socket.addMembership('239.255.0.1')               // join multicast group
})
Multicast uses the 224.0.0.0–239.255.255.255 range
Multicast group addresses live in the `224.0.0.0/4` block. Receivers `addMembership(group)` to subscribe; senders just `send` to the group address and every member receives it — without the sender knowing who they are. This decoupling is why multicast underpins discovery protocols. Broadcast (`255.255.255.255` or a subnet broadcast) is coarser and usually limited to the local network by routers.
Datagram size and fragmentation
Keep datagrams small — large ones fragment and drop more easily
A UDP datagram can theoretically be up to ~65 KB, but anything larger than the network's **MTU** (typically ~1500 bytes on Ethernet) gets fragmented into multiple IP packets — and if *any* fragment is lost, the **entire datagram** is discarded. So big datagrams have a much higher effective loss rate. For reliability, keep payloads under ~1400 bytes (a common safe MTU-minus-headers figure), or use TCP for bulk data.
When to choose UDP
  • Real-time media — voice/video where a late packet is worse than a lost one.

  • Gaming — frequent position updates; the next one supersedes a dropped one.

  • Telemetry / metrics — high-volume stats (e.g. StatsD) where occasional loss is fine.

  • Discovery & DNS — small request/response, broadcast/multicast.

  • Not for — anything requiring every byte intact and in order (use TCP/HTTP).

Next
Turning hostnames into the IP addresses these sockets need: [DNS Lookups (dns Module)](/nodejs/dns-module).