UDP: User Datagram Protocol Fast, Lightweight, Connectionless Transport

 

Chapter 58 โ€“ UDP: User Datagram Protocol
Part 3 of 8 ย |ย  Fast, Lightweight, Connectionless Transport
โšก Low Latency
๐Ÿ“ญ Unreliable
๐ŸŽฎ Real-Time Apps
๐Ÿ“ก Multicast

UDP โ€” The Speed Demon of Transport Protocols

UDP (User Datagram Protocol) is one of the two main transport-layer protocols. It wraps your data in a very thin envelope and sends it immediately โ€” no setup, no waiting for acknowledgments.

Think of UDP like sending a postcard โ€” you write it, drop it in the box, and move on. You do not know if it arrived. You do not wait. That is perfect when speed matters more than guaranteed delivery, like video calls, online gaming, or DNS lookups.

Key Terms:

UDP SOCK_DGRAM Datagram Unreliable Connectionless Port Number Broadcast Multicast sendto/recvfrom

๐Ÿ“‹ UDP Header โ€” Beautifully Simple (8 bytes only)

UDP’s entire header is just 8 bytes. Compare that to TCP’s 20+ byte header. This simplicity is exactly why UDP is fast โ€” minimal overhead, maximum speed.

Source Port (16 bits) Destination Port (16 bits)
Length (16 bits)
header + data in bytes
Checksum (16 bits)
error detection (optional in IPv4)
Data (variable length)
That’s it! Only 4 fields:
โ€ข Source Port โ€” which port the sender is using (optional for UDP โ€” can be 0 if no reply needed)
โ€ข Destination Port โ€” which port on the receiver should get this
โ€ข Length โ€” total size of UDP header + data (minimum 8 bytes = header only)
โ€ข Checksum โ€” basic error detection (mandatory in IPv6, optional in IPv4)

โšก How UDP Works โ€” Key Properties
๐Ÿ“ญ No Connection Setup

UDP just sends. No 3-way handshake. No “are you there?” check. First packet goes immediately. Great when you have many short transactions (like DNS: 1 question, 1 answer).

โŒ No Guaranteed Delivery

Packet can be silently dropped. No retransmit. No ACK. Application must handle lost packets itself if it cares.

๐Ÿ”€ No Ordering

Packets may arrive out of order. UDP delivers them as they come. Application must reorder if needed (e.g., RTP for video streaming adds its own sequence numbers).

๐Ÿ“ฆ Message Boundaries Preserved

Each sendto() produces exactly one datagram and one recvfrom(). Unlike TCP (a byte stream), UDP preserves message boundaries. If you send 100 bytes, you receive exactly 100 bytes in one call.

๐Ÿ“ก Supports Broadcast & Multicast

UDP can send one packet to all hosts on a subnet (broadcast) or to a group of interested hosts (multicast). TCP cannot do this (it is point-to-point only).

๐Ÿ“Š What Does UDP Add On Top of IP?

IP handles host-to-host delivery. UDP adds just two things on top of raw IP:

๐Ÿ”ข

Port Numbers

IP tells packets which machine to go to.
Ports tell the machine which process to hand it to.

Port 53 โ†’ DNS, Port 67 โ†’ DHCP, Port 123 โ†’ NTP

โœ…

Checksum

Detects bit errors in the datagram (header + data).

If checksum fails, the datagram is silently discarded. No error is sent to the application.

That’s it! UDP is literally just “IP + port numbers + optional checksum.” Very thin layer.

๐ŸŽฏ When to Use UDP โ€” Real-World Use Cases
Application Why UDP? Protocol
DNS Lookup 1 question โ†’ 1 answer. No need for connection setup overhead. Port 53
Video Streaming Slight frame loss is OK. Latency matters more than perfection. RTP over UDP
Online Gaming Game state must arrive ASAP. Old state is useless anyway. Custom UDP
DHCP Client has no IP yet โ€” must use broadcast. TCP is unicast only. Port 67/68
SNMP (Network Monitoring) Simple polls. Lightweight. Overhead of TCP not worth it. Port 161
QUIC (HTTP/3) Modern protocol builds reliability on top of UDP for better control. QUIC over UDP

๐Ÿ’ป Code: Simple UDP Echo Server & Client

A complete UDP server that echoes back whatever you send:

/* ============ udp_server.c ============ */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT   9090
#define BUFLEN 1024

int main(void) {
    int sockfd;
    struct sockaddr_in srv_addr, cli_addr;
    socklen_t cli_len;
    char buf[BUFLEN];
    ssize_t n;

    /* 1. Create UDP socket */
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) { perror("socket"); exit(1); }

    /* 2. Bind to port */
    memset(&srv_addr, 0, sizeof(srv_addr));
    srv_addr.sin_family      = AF_INET;
    srv_addr.sin_port        = htons(PORT);
    srv_addr.sin_addr.s_addr = INADDR_ANY;   /* listen on all interfaces */

    if (bind(sockfd, (struct sockaddr *)&srv_addr, sizeof(srv_addr)) < 0) {
        perror("bind"); exit(1);
    }

    printf("UDP server listening on port %d...\n", PORT);

    /* 3. Receive loop โ€” no accept() needed for UDP! */
    for (;;) {
        cli_len = sizeof(cli_addr);
        n = recvfrom(sockfd, buf, BUFLEN - 1, 0,
                     (struct sockaddr *)&cli_addr, &cli_len);
        if (n < 0) { perror("recvfrom"); continue; }

        buf[n] = '\0';
        char cli_ip[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &cli_addr.sin_addr, cli_ip, sizeof(cli_ip));
        printf("Received from %s:%d โ†’ \"%s\"\n",
               cli_ip, ntohs(cli_addr.sin_port), buf);

        /* 4. Echo back to sender */
        if (sendto(sockfd, buf, n, 0,
                   (struct sockaddr *)&cli_addr, cli_len) < 0) {
            perror("sendto");
        }
    }

    close(sockfd);
    return 0;
}
/* ============ udp_client.c ============ */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>

#define SERVER_IP  "127.0.0.1"
#define PORT       9090
#define BUFLEN     1024

int main(void) {
    int sockfd;
    struct sockaddr_in srv_addr;
    char buf[BUFLEN];
    ssize_t n;

    /* 1. Create UDP socket */
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) { perror("socket"); exit(1); }

    memset(&srv_addr, 0, sizeof(srv_addr));
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_port   = htons(PORT);
    inet_pton(AF_INET, SERVER_IP, &srv_addr.sin_addr);

    /* 2. Send a message โ€” no connect() needed! */
    const char *msg = "Hello UDP Server!";
    if (sendto(sockfd, msg, strlen(msg), 0,
               (struct sockaddr *)&srv_addr, sizeof(srv_addr)) < 0) {
        perror("sendto"); exit(1);
    }

    /* 3. Receive echo */
    n = recvfrom(sockfd, buf, BUFLEN - 1, 0, NULL, NULL);
    if (n < 0) { perror("recvfrom"); exit(1); }
    buf[n] = '\0';
    printf("Echo received: \"%s\"\n", buf);

    close(sockfd);
    return 0;
}
/* Build and run:
   gcc -o udp_server udp_server.c && ./udp_server &
   gcc -o udp_client udp_client.c && ./udp_client

   Server output: Received from 127.0.0.1:XXXXX โ†’ "Hello UDP Server!"
   Client output: Echo received: "Hello UDP Server!"
*/
๐Ÿ”‘ Key differences from TCP socket code:
โ€ข Use SOCK_DGRAM instead of SOCK_STREAM
โ€ข Server does NOT call listen() or accept()
โ€ข Use sendto() / recvfrom() instead of send() / recv()
โ€ข Each sendto() is one independent datagram
โ€ข No connection means any client can send to the server at any time

๐ŸŽฏ Interview Questions โ€” UDP Protocol
Q1: What does UDP provide that raw IP does not?

A: UDP adds two things to IP: (1) Port numbers โ€” allowing multiple processes on the same machine to send/receive independently. (2) An optional checksum for error detection of the datagram. That’s all. UDP is essentially a thin wrapper around IP.

Q2: UDP is unreliable โ€” so why do applications use it instead of TCP?

A: Several reasons: (1) Speed โ€” no connection setup, no ACK waiting, lower latency. (2) Real-time โ€” for video/audio streaming, old data is useless; better to drop than delay. (3) Broadcast/multicast โ€” TCP is unicast only; UDP can send to multiple receivers at once. (4) Less overhead โ€” 8-byte header vs TCP’s 20+ bytes. (5) Application-controlled reliability โ€” application can implement exactly the reliability it needs (e.g., QUIC does this on top of UDP).

Q3: What is the difference between SOCK_DGRAM and SOCK_STREAM?

A: SOCK_DGRAM creates a UDP socket โ€” connectionless, unreliable, message-boundary-preserving, uses sendto/recvfrom. SOCK_STREAM creates a TCP socket โ€” connection-oriented, reliable, ordered, byte-stream (no message boundaries), uses send/recv.

Q4: Does UDP preserve message boundaries?

A: Yes. This is a key difference from TCP. Each sendto() produces exactly one datagram, and each recvfrom() reads exactly one datagram. If you send 100 bytes and 200 bytes in two calls, the receiver gets two separate recvfrom() returns of 100 and 200 bytes โ€” never merged together. TCP, being a byte stream, does NOT preserve message boundaries.

Q5: Can you use connect() with a UDP socket? What does it do?

A: Yes. Calling connect() on a UDP socket does NOT create a real connection. It just records the peer address so you can use send()/recv() instead of sendto()/recvfrom(). It also causes the kernel to only receive datagrams from that peer address (filters others). The socket remains connectionless โ€” no handshake occurs. You can call connect() multiple times to change the peer or use AF_UNSPEC to disconnect.

Q6: What is the maximum size of a UDP datagram?

A: The theoretical maximum is 65,507 bytes (65,535 bytes max UDP datagram size minus 8 bytes UDP header minus 20 bytes IP header). However, in practice, datagrams larger than the path MTU (usually ~1472 bytes for Ethernet after headers) will be fragmented at the IP layer, which is inefficient. Applications that care about performance keep UDP payloads under ~1472 bytes.

Leave a Reply

Your email address will not be published. Required fields are marked *