Back to Blog
Node.jsdgramUDPNetworking

Node.js dgram Module Guide

Master the Node.js dgram module for UDP networking including datagram sockets and multicast.

B
Bootspring Team
Engineering
August 31, 2019
7 min read

The dgram module provides UDP datagram sockets for connectionless networking. Here's how to use it.

Basic UDP Server#

1import dgram from 'node:dgram'; 2 3const server = dgram.createSocket('udp4'); 4 5server.on('error', (err) => { 6 console.error('Server error:', err); 7 server.close(); 8}); 9 10server.on('message', (msg, rinfo) => { 11 console.log(`Received: ${msg} from ${rinfo.address}:${rinfo.port}`); 12 13 // Send response 14 const response = Buffer.from('Message received'); 15 server.send(response, rinfo.port, rinfo.address); 16}); 17 18server.on('listening', () => { 19 const address = server.address(); 20 console.log(`Server listening on ${address.address}:${address.port}`); 21}); 22 23server.bind(41234);

Basic UDP Client#

1import dgram from 'node:dgram'; 2 3const client = dgram.createSocket('udp4'); 4 5const message = Buffer.from('Hello, server!'); 6 7client.send(message, 41234, 'localhost', (err) => { 8 if (err) { 9 console.error('Send error:', err); 10 client.close(); 11 return; 12 } 13 console.log('Message sent'); 14}); 15 16client.on('message', (msg, rinfo) => { 17 console.log(`Response: ${msg} from ${rinfo.address}:${rinfo.port}`); 18 client.close(); 19}); 20 21// Set timeout for response 22setTimeout(() => { 23 console.log('No response received'); 24 client.close(); 25}, 5000);

UDP Echo Server#

1import dgram from 'node:dgram'; 2 3const server = dgram.createSocket('udp4'); 4 5server.on('message', (msg, rinfo) => { 6 console.log(`Echo: ${msg}`); 7 8 // Echo back the message 9 server.send(msg, rinfo.port, rinfo.address, (err) => { 10 if (err) console.error('Echo error:', err); 11 }); 12}); 13 14server.bind(41234, () => { 15 console.log('Echo server running on port 41234'); 16});

IPv6 Support#

1import dgram from 'node:dgram'; 2 3// IPv6 socket 4const server6 = dgram.createSocket('udp6'); 5 6server6.on('message', (msg, rinfo) => { 7 console.log(`IPv6 message: ${msg} from [${rinfo.address}]:${rinfo.port}`); 8}); 9 10server6.bind(41234, '::', () => { 11 console.log('IPv6 server running'); 12}); 13 14// Dual-stack socket (IPv4 and IPv6) 15const dualStack = dgram.createSocket({ 16 type: 'udp6', 17 ipv6Only: false, 18}); 19 20dualStack.bind(41235, () => { 21 console.log('Dual-stack server running'); 22});

Multicast#

1import dgram from 'node:dgram'; 2 3const MULTICAST_ADDR = '224.1.1.1'; 4const PORT = 5000; 5 6// Multicast sender 7function createSender() { 8 const sender = dgram.createSocket('udp4'); 9 10 setInterval(() => { 11 const message = Buffer.from(`Time: ${Date.now()}`); 12 sender.send(message, PORT, MULTICAST_ADDR, (err) => { 13 if (err) console.error('Multicast send error:', err); 14 }); 15 }, 1000); 16 17 return sender; 18} 19 20// Multicast receiver 21function createReceiver() { 22 const receiver = dgram.createSocket({ 23 type: 'udp4', 24 reuseAddr: true, 25 }); 26 27 receiver.on('message', (msg, rinfo) => { 28 console.log(`Multicast: ${msg} from ${rinfo.address}`); 29 }); 30 31 receiver.bind(PORT, () => { 32 receiver.addMembership(MULTICAST_ADDR); 33 console.log('Joined multicast group'); 34 }); 35 36 return receiver; 37} 38 39const sender = createSender(); 40const receiver = createReceiver();

Broadcast#

1import dgram from 'node:dgram'; 2 3const BROADCAST_ADDR = '255.255.255.255'; 4const PORT = 5001; 5 6// Broadcast sender 7function createBroadcaster() { 8 const socket = dgram.createSocket('udp4'); 9 10 socket.bind(() => { 11 socket.setBroadcast(true); 12 13 setInterval(() => { 14 const message = Buffer.from('Broadcast message'); 15 socket.send(message, PORT, BROADCAST_ADDR, (err) => { 16 if (err) console.error('Broadcast error:', err); 17 }); 18 }, 1000); 19 }); 20 21 return socket; 22} 23 24// Broadcast receiver 25function createBroadcastReceiver() { 26 const socket = dgram.createSocket({ 27 type: 'udp4', 28 reuseAddr: true, 29 }); 30 31 socket.on('message', (msg, rinfo) => { 32 console.log(`Broadcast received: ${msg} from ${rinfo.address}`); 33 }); 34 35 socket.bind(PORT); 36 37 return socket; 38}

Socket Options#

1import dgram from 'node:dgram'; 2 3const socket = dgram.createSocket({ 4 type: 'udp4', 5 reuseAddr: true, // Allow multiple sockets on same port 6 recvBufferSize: 65536, // Receive buffer size 7 sendBufferSize: 65536, // Send buffer size 8}); 9 10socket.bind(41234, () => { 11 // Set TTL (Time To Live) 12 socket.setTTL(128); 13 14 // Set multicast TTL 15 socket.setMulticastTTL(2); 16 17 // Enable multicast loopback 18 socket.setMulticastLoopback(true); 19 20 // Get/set receive buffer size 21 console.log('Recv buffer:', socket.getRecvBufferSize()); 22 socket.setRecvBufferSize(131072); 23 24 // Get/set send buffer size 25 console.log('Send buffer:', socket.getSendBufferSize()); 26 socket.setSendBufferSize(131072); 27});

Message Framing#

1import dgram from 'node:dgram'; 2 3// Simple message protocol 4class UDPProtocol { 5 constructor(socket) { 6 this.socket = socket; 7 this.handlers = new Map(); 8 } 9 10 // Send structured message 11 send(type, data, port, address) { 12 const message = JSON.stringify({ type, data }); 13 const buffer = Buffer.from(message); 14 15 return new Promise((resolve, reject) => { 16 this.socket.send(buffer, port, address, (err) => { 17 if (err) reject(err); 18 else resolve(); 19 }); 20 }); 21 } 22 23 // Register message handler 24 on(type, handler) { 25 this.handlers.set(type, handler); 26 } 27 28 // Process incoming messages 29 listen() { 30 this.socket.on('message', (msg, rinfo) => { 31 try { 32 const { type, data } = JSON.parse(msg.toString()); 33 const handler = this.handlers.get(type); 34 if (handler) { 35 handler(data, rinfo); 36 } 37 } catch (err) { 38 console.error('Parse error:', err); 39 } 40 }); 41 } 42} 43 44// Usage 45const socket = dgram.createSocket('udp4'); 46const protocol = new UDPProtocol(socket); 47 48protocol.on('ping', (data, rinfo) => { 49 console.log('Ping received from', rinfo.address); 50 protocol.send('pong', { time: Date.now() }, rinfo.port, rinfo.address); 51}); 52 53protocol.on('pong', (data, rinfo) => { 54 console.log('Pong received:', data.time); 55}); 56 57protocol.listen(); 58socket.bind(41234);

Service Discovery#

1import dgram from 'node:dgram'; 2import os from 'node:os'; 3 4const DISCOVERY_PORT = 5002; 5const MULTICAST_ADDR = '224.0.0.1'; 6 7class ServiceDiscovery { 8 constructor(serviceName, servicePort) { 9 this.serviceName = serviceName; 10 this.servicePort = servicePort; 11 this.socket = dgram.createSocket({ 12 type: 'udp4', 13 reuseAddr: true, 14 }); 15 this.peers = new Map(); 16 } 17 18 start() { 19 this.socket.on('message', (msg, rinfo) => { 20 try { 21 const data = JSON.parse(msg.toString()); 22 this.handleMessage(data, rinfo); 23 } catch (err) { 24 // Ignore invalid messages 25 } 26 }); 27 28 this.socket.bind(DISCOVERY_PORT, () => { 29 this.socket.addMembership(MULTICAST_ADDR); 30 this.socket.setBroadcast(true); 31 32 // Announce presence 33 this.announce(); 34 35 // Periodic announcements 36 setInterval(() => this.announce(), 5000); 37 38 // Clean up stale peers 39 setInterval(() => this.cleanupPeers(), 10000); 40 }); 41 } 42 43 announce() { 44 const message = JSON.stringify({ 45 type: 'announce', 46 service: this.serviceName, 47 port: this.servicePort, 48 hostname: os.hostname(), 49 }); 50 51 this.socket.send( 52 Buffer.from(message), 53 DISCOVERY_PORT, 54 MULTICAST_ADDR 55 ); 56 } 57 58 handleMessage(data, rinfo) { 59 if (data.type === 'announce') { 60 const key = `${rinfo.address}:${data.port}`; 61 this.peers.set(key, { 62 address: rinfo.address, 63 port: data.port, 64 service: data.service, 65 hostname: data.hostname, 66 lastSeen: Date.now(), 67 }); 68 } 69 } 70 71 cleanupPeers() { 72 const now = Date.now(); 73 for (const [key, peer] of this.peers) { 74 if (now - peer.lastSeen > 15000) { 75 this.peers.delete(key); 76 console.log('Peer removed:', key); 77 } 78 } 79 } 80 81 getPeers() { 82 return Array.from(this.peers.values()); 83 } 84} 85 86// Usage 87const discovery = new ServiceDiscovery('my-app', 8080); 88discovery.start(); 89 90setInterval(() => { 91 console.log('Known peers:', discovery.getPeers()); 92}, 5000);

UDP with Promises#

1import dgram from 'node:dgram'; 2 3class UDPClient { 4 constructor() { 5 this.socket = dgram.createSocket('udp4'); 6 this.pending = new Map(); 7 this.messageId = 0; 8 9 this.socket.on('message', (msg, rinfo) => { 10 try { 11 const data = JSON.parse(msg.toString()); 12 const pending = this.pending.get(data.id); 13 if (pending) { 14 clearTimeout(pending.timeout); 15 this.pending.delete(data.id); 16 pending.resolve(data.response); 17 } 18 } catch (err) { 19 // Ignore parse errors 20 } 21 }); 22 } 23 24 send(message, port, address, timeout = 5000) { 25 return new Promise((resolve, reject) => { 26 const id = ++this.messageId; 27 28 const timeoutId = setTimeout(() => { 29 this.pending.delete(id); 30 reject(new Error('Request timeout')); 31 }, timeout); 32 33 this.pending.set(id, { 34 resolve, 35 reject, 36 timeout: timeoutId, 37 }); 38 39 const data = JSON.stringify({ id, request: message }); 40 this.socket.send(Buffer.from(data), port, address, (err) => { 41 if (err) { 42 clearTimeout(timeoutId); 43 this.pending.delete(id); 44 reject(err); 45 } 46 }); 47 }); 48 } 49 50 close() { 51 this.socket.close(); 52 } 53} 54 55// Usage 56const client = new UDPClient(); 57 58async function main() { 59 try { 60 const response = await client.send( 61 { action: 'getData' }, 62 41234, 63 'localhost' 64 ); 65 console.log('Response:', response); 66 } catch (err) { 67 console.error('Error:', err.message); 68 } finally { 69 client.close(); 70 } 71}

Error Handling#

1import dgram from 'node:dgram'; 2 3const socket = dgram.createSocket('udp4'); 4 5socket.on('error', (err) => { 6 console.error('Socket error:', err.message); 7 8 // Handle specific errors 9 switch (err.code) { 10 case 'EADDRINUSE': 11 console.log('Port already in use'); 12 break; 13 case 'EACCES': 14 console.log('Permission denied'); 15 break; 16 case 'ENETUNREACH': 17 console.log('Network unreachable'); 18 break; 19 default: 20 console.log('Unknown error:', err.code); 21 } 22 23 socket.close(); 24}); 25 26socket.on('close', () => { 27 console.log('Socket closed'); 28}); 29 30// Graceful shutdown 31process.on('SIGINT', () => { 32 console.log('Shutting down...'); 33 socket.close(() => { 34 process.exit(0); 35 }); 36}); 37 38socket.bind(41234);

Best Practices#

Socket Configuration: ✓ Use reuseAddr for multiple listeners ✓ Set appropriate buffer sizes ✓ Handle all error events ✓ Clean up on close Message Handling: ✓ Validate incoming data ✓ Implement timeouts ✓ Handle partial messages ✓ Use sequence numbers Multicast: ✓ Join/leave groups properly ✓ Set appropriate TTL ✓ Handle loopback ✓ Use reuseAddr Avoid: ✗ Ignoring errors ✗ Assuming delivery ✗ Large datagrams (>512 bytes safe) ✗ Blocking operations

Conclusion#

The Node.js dgram module provides UDP networking for connectionless communication. Use it for real-time applications, service discovery, broadcasts, and multicast scenarios where occasional packet loss is acceptable. Always handle errors, implement timeouts for request-response patterns, and keep messages small for reliable delivery. For guaranteed delivery or streaming data, consider TCP with the net module instead.

Share this article

Help spread the word about Bootspring