UDP datagrams

Send a UDP datagram

Send one message, close the client after completion, and observe the absence of a handshake.

10 minute lesson

~~~

The UDP client is even smaller than the server. A UDP sender can transmit without first proving a receiver exists — there’s no handshake, so there’s nothing to wait for before sending.

Create udp-client.mjs:

import dgram from 'node:dgram'

const socket = dgram.createSocket('udp4')
const message = Buffer.from('hello')

socket.send(message, 41234, '127.0.0.1', error => {
  if (error) console.error(error)
  socket.close()
})

socket.send() takes the payload, the destination port, and the destination address. The callback fires when the datagram has been handed off. Success from send() means local handling completed — the kernel accepted the datagram for transmission. It says nothing about arrival, and even less about the receiving application processing it.

The socket.close() in the callback matters for a different reason: an open dgram socket keeps the Node event loop alive, so without it the client never exits.

Run it against a dead port

Run it with the server up:

node udp-client.mjs
# server terminal prints: hello { address: '127.0.0.1', ... }

Now stop the server and run the client again:

node udp-client.mjs
# ...nothing. No error. Exit code 0.

Compare what the sender can prove in each case: nothing differs from the sender’s point of view. Contrast this with TCP, where connecting to a dead port failed fast with ECONNREFUSED. The UDP client reports success either way, because success only ever meant “sent”.

Some systems generate an ICMP port-unreachable error when the destination port is closed, but you can’t depend on receiving it, and firewalls often drop it. Design as if no error will ever come back.

Confirmation is your job

That’s the deal you accept with UDP: fast, connectionless, and silent about failure. Applications needing confirmation must define acknowledgments, deadlines, and retry limits themselves. The receiver sends a reply datagram; the sender waits with a deadline and resends a bounded number of times:

send request -> wait up to 500 ms for reply
no reply     -> resend, at most 3 attempts
still nothing -> report failure to the caller

The next two lessons build exactly this: request identifiers so retries can be recognized, and explicit handling for loss and reordering.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →