Networking

UDP

User Datagram Protocol (UDP) is a connectionless transport layer protocol that sends data packets without establishing a connection, offering low latency and minimal overhead for applications like gaming, streaming, and DNS.

What is UDP?

UDP (User Datagram Protocol) is a connectionless, lightweight transport layer protocol that sends data packets (datagrams) without establishing a connection or guaranteeing delivery. Unlike TCP’s reliable, ordered delivery, UDP prioritizes speed and low latency, making it ideal for real-time applications where occasional packet loss is acceptable.

UDP vs TCP

Protocol Comparison

interface TransportProtocolComparison {
  udp: {
    connection: 'Connectionless';
    reliability: 'Best-effort (no guarantees)';
    ordering: 'No guaranteed order';
    overhead: 'Minimal (8-byte header)';
    speed: 'Very fast';
    flowControl: 'None';
    congestionControl: 'None';
    useCases: ['Gaming', 'Live streaming', 'DNS', 'VoIP', 'IoT'];
    advantages: ['Low latency', 'Fast', 'Simple', 'No handshake delay'];
    disadvantages: ['No reliability', 'Packet loss', 'No ordering', 'No congestion control'];
  };
  tcp: {
    connection: 'Connection-oriented';
    reliability: 'Guaranteed delivery';
    ordering: 'Sequential ordering';
    overhead: 'Higher (20+ byte header)';
    speed: 'Slower (overhead for reliability)';
    flowControl: 'Yes';
    congestionControl: 'Yes';
    useCases: ['HTTP', 'File transfer', 'Email', 'Database connections'];
    advantages: ['Reliable', 'Ordered', 'Error checking', 'Flow control'];
    disadvantages: ['Slower', 'Higher overhead', 'Connection setup time'];
  };
}

UDP Packet Structure

Datagram Format

UDP Datagram Structure (8 bytes header):

 0                   15 16                  31
+-----------------------+-----------------------+
|     Source Port       |   Destination Port    |
+-----------------------+-----------------------+
|        Length         |      Checksum         |
+-----------------------+-----------------------+
|                                               |
|              Data (Payload)                   |
|                                               |
+-----------------------------------------------+

Fields:
- Source Port: 16 bits (optional, can be 0)
- Destination Port: 16 bits
- Length: 16 bits (header + data)
- Checksum: 16 bits (optional in IPv4, mandatory in IPv6)

UDP in Node.js

Basic UDP Server

import dgram from 'dgram';

class UDPServer {
  private socket: dgram.Socket;
  private port: number;

  constructor(port: number) {
    this.port = port;
    this.socket = dgram.createSocket('udp4');
  }

  start() {
    this.socket.on('message', (msg, rinfo) => {
      console.log(`Received from ${rinfo.address}:${rinfo.port}: ${msg.toString()}`);

      // Echo back to client
      this.socket.send(msg, rinfo.port, rinfo.address, (err) => {
        if (err) {
          console.error('Send error:', err);
        }
      });
    });

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

    this.socket.on('error', (err) => {
      console.error('Server error:', err);
      this.socket.close();
    });

    this.socket.bind(this.port);
  }

  send(message: string, port: number, address: string) {
    const buffer = Buffer.from(message);

    this.socket.send(buffer, port, address, (err) => {
      if (err) {
        console.error('Send error:', err);
      } else {
        console.log(`Sent to ${address}:${port}: ${message}`);
      }
    });
  }

  close() {
    this.socket.close();
  }
}

// Usage
const server = new UDPServer(41234);
server.start();

UDP Client

import dgram from 'dgram';

class UDPClient {
  private socket: dgram.Socket;

  constructor() {
    this.socket = dgram.createSocket('udp4');

    this.socket.on('message', (msg, rinfo) => {
      console.log(`Response from ${rinfo.address}:${rinfo.port}: ${msg.toString()}`);
    });

    this.socket.on('error', (err) => {
      console.error('Client error:', err);
      this.socket.close();
    });
  }

  send(message: string, port: number, address: string): Promise<void> {
    return new Promise((resolve, reject) => {
      const buffer = Buffer.from(message);

      this.socket.send(buffer, port, address, (err) => {
        if (err) {
          reject(err);
        } else {
          console.log(`Sent: ${message}`);
          resolve();
        }
      });
    });
  }

  async sendWithResponse(
    message: string,
    port: number,
    address: string,
    timeout: number = 5000
  ): Promise<string> {
    return new Promise((resolve, reject) => {
      const timeoutId = setTimeout(() => {
        this.socket.removeAllListeners('message');
        reject(new Error('Response timeout'));
      }, timeout);

      this.socket.once('message', (msg) => {
        clearTimeout(timeoutId);
        resolve(msg.toString());
      });

      this.send(message, port, address).catch(reject);
    });
  }

  close() {
    this.socket.close();
  }
}

// Usage
const client = new UDPClient();

try {
  const response = await client.sendWithResponse(
    'Hello UDP Server',
    41234,
    'localhost'
  );
  console.log('Response:', response);
} finally {
  client.close();
}

UDP Use Cases

DNS Queries (UDP Port 53)

import dgram from 'dgram';
import { Buffer } from 'buffer';

class SimpleDNSClient {
  private socket: dgram.Socket;

  constructor() {
    this.socket = dgram.createSocket('udp4');
  }

  async query(domain: string, dnsServer: string = '8.8.8.8'): Promise<string[]> {
    // Build DNS query packet
    const query = this.buildDNSQuery(domain);

    // Send query to DNS server (port 53)
    this.socket.send(query, 53, dnsServer);

    // Wait for response
    return new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error('DNS query timeout'));
      }, 5000);

      this.socket.once('message', (response) => {
        clearTimeout(timeout);

        try {
          const addresses = this.parseDNSResponse(response);
          resolve(addresses);
        } catch (error) {
          reject(error);
        }
      });
    });
  }

  private buildDNSQuery(domain: string): Buffer {
    // Simplified DNS query builder
    // Real implementation would be more complex

    const query: number[] = [];

    // Header (12 bytes)
    query.push(0x12, 0x34); // Transaction ID
    query.push(0x01, 0x00); // Flags (standard query)
    query.push(0x00, 0x01); // Questions: 1
    query.push(0x00, 0x00); // Answer RRs: 0
    query.push(0x00, 0x00); // Authority RRs: 0
    query.push(0x00, 0x00); // Additional RRs: 0

    // Question
    const labels = domain.split('.');
    for (const label of labels) {
      query.push(label.length);
      for (let i = 0; i < label.length; i++) {
        query.push(label.charCodeAt(i));
      }
    }
    query.push(0x00); // End of domain name

    query.push(0x00, 0x01); // Type: A (IPv4)
    query.push(0x00, 0x01); // Class: IN (Internet)

    return Buffer.from(query);
  }

  private parseDNSResponse(response: Buffer): string[] {
    // Simplified DNS response parser
    // Real implementation would be more complex
    const addresses: string[] = [];

    // Skip header and question sections
    // Parse answer section for A records

    // Example: extract IPv4 addresses
    // This is a placeholder - real DNS parsing is complex

    return addresses;
  }

  close() {
    this.socket.close();
  }
}

// Usage
const dnsClient = new SimpleDNSClient();
const addresses = await dnsClient.query('example.com');
console.log('IP addresses:', addresses);
dnsClient.close();

Real-Time Gaming

interface GamePacket {
  playerId: string;
  timestamp: number;
  position: { x: number; y: number; z: number };
  action: string;
}

class GameServer {
  private socket: dgram.Socket;
  private players: Map<string, {
    address: string;
    port: number;
    lastUpdate: number;
    position: any;
  }>;

  constructor(port: number) {
    this.socket = dgram.createSocket('udp4');
    this.players = new Map();

    this.socket.bind(port);

    this.socket.on('message', (msg, rinfo) => {
      try {
        const packet: GamePacket = JSON.parse(msg.toString());
        this.handleGamePacket(packet, rinfo);
      } catch (error) {
        console.error('Invalid packet:', error);
      }
    });

    // Remove inactive players
    setInterval(() => {
      this.cleanupInactivePlayers();
    }, 10000);
  }

  private handleGamePacket(packet: GamePacket, rinfo: dgram.RemoteInfo) {
    const { playerId, position, action } = packet;

    // Update player state
    this.players.set(playerId, {
      address: rinfo.address,
      port: rinfo.port,
      lastUpdate: Date.now(),
      position
    });

    // Broadcast to other players
    this.broadcastState(playerId);
  }

  private broadcastState(excludePlayer?: string) {
    const state = {
      timestamp: Date.now(),
      players: Array.from(this.players.entries())
        .filter(([id]) => id !== excludePlayer)
        .map(([id, data]) => ({
          id,
          position: data.position
        }))
    };

    const message = Buffer.from(JSON.stringify(state));

    // Send to all players
    for (const [playerId, player] of this.players.entries()) {
      if (playerId !== excludePlayer) {
        this.socket.send(message, player.port, player.address);
      }
    }
  }

  private cleanupInactivePlayers() {
    const now = Date.now();
    const timeout = 30000; // 30 seconds

    for (const [playerId, player] of this.players.entries()) {
      if (now - player.lastUpdate > timeout) {
        console.log(`Removing inactive player: ${playerId}`);
        this.players.delete(playerId);
      }
    }
  }
}

// Game client
class GameClient {
  private socket: dgram.Socket;
  private playerId: string;
  private serverAddress: string;
  private serverPort: number;

  constructor(playerId: string, serverAddress: string, serverPort: number) {
    this.socket = dgram.createSocket('udp4');
    this.playerId = playerId;
    this.serverAddress = serverAddress;
    this.serverPort = serverPort;

    this.socket.on('message', (msg) => {
      const state = JSON.parse(msg.toString());
      this.updateGameState(state);
    });
  }

  sendPosition(x: number, y: number, z: number, action: string) {
    const packet: GamePacket = {
      playerId: this.playerId,
      timestamp: Date.now(),
      position: { x, y, z },
      action
    };

    const message = Buffer.from(JSON.stringify(packet));
    this.socket.send(message, this.serverPort, this.serverAddress);
  }

  private updateGameState(state: any) {
    // Update local game state with other players' positions
    console.log('Game state update:', state);
  }
}

VoIP (Voice over IP)

class VoIPConnection {
  private socket: dgram.Socket;
  private remoteAddress: string;
  private remotePort: number;

  constructor(remoteAddress: string, remotePort: number) {
    this.socket = dgram.createSocket('udp4');
    this.remoteAddress = remoteAddress;
    this.remotePort = remotePort;

    this.socket.on('message', (msg) => {
      this.handleAudioPacket(msg);
    });
  }

  sendAudioChunk(audioData: Buffer) {
    // Add RTP header (simplified)
    const rtpPacket = this.encodeRTP(audioData);

    // Send immediately - no waiting for ACK
    this.socket.send(rtpPacket, this.remotePort, this.remoteAddress);
  }

  private encodeRTP(audioData: Buffer): Buffer {
    // RTP (Real-time Transport Protocol) header
    const header = Buffer.alloc(12);

    header.writeUInt8(0x80, 0);  // Version 2, no padding, no extension
    header.writeUInt8(0x00, 1);  // Payload type
    header.writeUInt16BE(0, 2);  // Sequence number
    header.writeUInt32BE(Date.now(), 4); // Timestamp
    header.writeUInt32BE(0, 8);  // SSRC identifier

    return Buffer.concat([header, audioData]);
  }

  private handleAudioPacket(packet: Buffer) {
    // Parse RTP header and extract audio data
    const audioData = packet.slice(12); // Skip 12-byte RTP header

    // Play audio
    this.playAudio(audioData);
  }

  private playAudio(audioData: Buffer) {
    // Send to audio output device
    console.log(`Playing audio chunk: ${audioData.length} bytes`);
  }
}

UDP with SOCKS5 Proxy

SOCKS5 UDP Association

class SOCKS5UDPClient {
  private controlSocket: any; // TCP control connection
  private udpSocket: dgram.Socket;
  private relayAddress: string = '';
  private relayPort: number = 0;

  async connect(
    proxyHost: string,
    proxyPort: number,
    username?: string,
    password?: string
  ): Promise<void> {
    // 1. Establish TCP control connection
    // 2. Authenticate
    // 3. Send UDP ASSOCIATE command
    // 4. Receive relay address/port

    console.log('UDP relay configured:', this.relayAddress, this.relayPort);

    // Create UDP socket for actual data
    this.udpSocket = dgram.createSocket('udp4');

    this.udpSocket.on('message', (msg, rinfo) => {
      // Parse SOCKS5 UDP packet
      this.handleUDPResponse(msg);
    });
  }

  sendUDPPacket(data: Buffer, targetHost: string, targetPort: number) {
    // Encapsulate in SOCKS5 UDP format
    const socks5Packet = this.encapsulateUDP(data, targetHost, targetPort);

    // Send to SOCKS5 relay
    this.udpSocket.send(socks5Packet, this.relayPort, this.relayAddress);
  }

  private encapsulateUDP(data: Buffer, host: string, port: number): Buffer {
    // SOCKS5 UDP packet format:
    // +----+------+------+----------+----------+----------+
    // |RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |
    // +----+------+------+----------+----------+----------+

    const header = Buffer.alloc(10);
    header.writeUInt16BE(0, 0);      // Reserved
    header.writeUInt8(0, 2);         // Fragment number
    header.writeUInt8(0x03, 3);      // ATYP: Domain name
    header.writeUInt8(host.length, 4); // Domain length

    const hostBuffer = Buffer.from(host);
    const portBuffer = Buffer.alloc(2);
    portBuffer.writeUInt16BE(port, 0);

    return Buffer.concat([
      header.slice(0, 5),
      hostBuffer,
      portBuffer,
      data
    ]);
  }

  private handleUDPResponse(packet: Buffer) {
    // Parse SOCKS5 UDP response
    // Extract actual data
    const data = packet.slice(10); // Skip SOCKS5 header
    console.log('Received UDP data:', data);
  }
}

Best Practices

For Application Developers

interface UDPBestPractices {
  packetSize: {
    recommendation: 'Keep packets under 1472 bytes';
    reason: 'Avoid fragmentation (MTU - IP header - UDP header)';
    implementation: 'Chunk large data';
  };
  retransmission: {
    recommendation: 'Implement application-level retries';
    reason: 'UDP does not guarantee delivery';
    implementation: 'Sequence numbers and ACKs';
  };
  ordering: {
    recommendation: 'Add sequence numbers to packets';
    reason: 'UDP does not guarantee order';
    implementation: 'Reorder at application layer';
  };
  errorDetection: {
    recommendation: 'Implement additional checksums';
    reason: 'UDP checksum is optional in IPv4';
    implementation: 'CRC or application-level checksum';
  };
  congestionControl: {
    recommendation: 'Implement rate limiting';
    reason: 'UDP has no built-in congestion control';
    implementation: 'Token bucket or leaky bucket algorithm';
  };
}

Reliability Layer on UDP

class ReliableUDP {
  private socket: dgram.Socket;
  private pendingACKs: Map<number, {
    data: Buffer;
    timestamp: number;
    retries: number;
  }>;
  private sequenceNumber: number = 0;

  constructor() {
    this.socket = dgram.createSocket('udp4');
    this.pendingACKs = new Map();

    this.socket.on('message', (msg) => {
      this.handleMessage(msg);
    });

    // Retransmission timer
    setInterval(() => {
      this.retransmitLostPackets();
    }, 100);
  }

  async send(data: Buffer, port: number, address: string): Promise<void> {
    const seq = this.sequenceNumber++;

    // Add header with sequence number
    const packet = Buffer.alloc(4 + data.length);
    packet.writeUInt32BE(seq, 0);
    data.copy(packet, 4);

    // Store for potential retransmission
    this.pendingACKs.set(seq, {
      data: packet,
      timestamp: Date.now(),
      retries: 0
    });

    // Send packet
    this.socket.send(packet, port, address);
  }

  private handleMessage(msg: Buffer) {
    // Check if this is an ACK
    if (msg.length === 4) {
      const ackSeq = msg.readUInt32BE(0);
      this.pendingACKs.delete(ackSeq);
      console.log(`ACK received for packet ${ackSeq}`);
    } else {
      // Data packet - send ACK
      const seq = msg.readUInt32BE(0);
      this.sendACK(seq);

      // Process data
      const data = msg.slice(4);
      this.handleData(data);
    }
  }

  private sendACK(seq: number) {
    const ack = Buffer.alloc(4);
    ack.writeUInt32BE(seq, 0);
    // Send ACK to sender
  }

  private retransmitLostPackets() {
    const now = Date.now();
    const timeout = 1000; // 1 second

    for (const [seq, packet] of this.pendingACKs.entries()) {
      if (now - packet.timestamp > timeout) {
        if (packet.retries < 3) {
          // Retransmit
          console.log(`Retransmitting packet ${seq}`);
          packet.retries++;
          packet.timestamp = now;
          // Re-send packet
        } else {
          // Give up
          console.error(`Packet ${seq} lost after 3 retries`);
          this.pendingACKs.delete(seq);
        }
      }
    }
  }

  private handleData(data: Buffer) {
    console.log('Received data:', data);
  }
}

When to Use UDP

interface UDPUseCases {
  perfectFor: {
    realTimeGaming: 'Low latency more important than reliability';
    liveStreaming: 'Can tolerate some packet loss';
    voipVideo: 'Real-time communication';
    dns: 'Single request-response, can retry';
    iot: 'Low overhead, simple messages';
    broadcasting: 'One-to-many communication';
  };
  notGoodFor: {
    fileTransfer: 'Need reliability and ordering';
    webPages: 'Need guaranteed delivery';
    emails: 'Cannot lose data';
    financial: 'Reliability critical';
    database: 'Data integrity essential';
  };
}

Learn More

Create a free Account to fix CORS Errors in Production

Say goodbye to CORS errors and get back to building great web applications. It's free!

CORSPROXY Dashboard

Related Terms

More in Networking

Related guides

Back to Glossary