Siksha Sarovar

Siksha Sarovar (sikshasarovar.com) is a free educational web application that helps students in India learn programming and prepare for academic and competitive exams. The platform offers structured coding courses (C, C++, Python, Java, HTML, CSS, PHP, Power BI, AI, Machine Learning, Data Science), complete university curriculum notes for BCA/MCA students with previous year question papers, Class 10 and Class 12 CBSE/HBSE school notes, and dedicated preparation material for SSC, UPSC, Banking, Railway and other government exams. Browsing the site is completely free and requires no account. Users may optionally sign in with Google solely to save their learning progress, quiz scores and personal preferences across devices.

Privacy Policy | Terms of Service | Contact Siksha Sarovar | About Siksha Sarovar

v4.0.9 · PWA
Siksha Sarovar logo
Siksha Sarovar
Your Learning Universe

Siksha Sarovar is a free e-learning platform for coding courses, BCA university notes and competitive exam preparation. Optional Google sign-in saves your learning progress across devices.

Initializing knowledge base…
Compiling modules 0%

Unit 2: Elementary UDP Sockets

Lesson 6 of 15 in the free Network Programming notes on Siksha Sarovar, written by Rohit Jangra.

2.1 The UDP API — two new calls

No connections: each datagram carries its destination / reveals its source.

ssize_t recvfrom(int fd, void *buf, size_t n, int flags,
                 struct sockaddr *from, socklen_t *fromlen);   /* value-result! */
ssize_t sendto(int fd, const void *buf, size_t n, int flags,
               const struct sockaddr *to, socklen_t tolen);

No listen, no accept, no fork needed — one socket serves every client, datagrams from all of them interleave through the same recvfrom. The typical UDP server is iterative, not concurrent.

/* the UDP echo server's whole loop */
void dg_echo(int sockfd, struct sockaddr *cli, socklen_t clilen) {
    ssize_t n; socklen_t len; char buf[MAXLINE];
    for (;;) {
        len = clilen;
        n = recvfrom(sockfd, buf, MAXLINE, 0, cli, &len);
        sendto(sockfd, buf, n, 0, cli, len);
    }
}

A 0-byte datagram is legal (recvfrom returns 0 — not EOF; there is no EOF on UDP).

2.2 The lost-datagram problem

Client sends; the datagram (or the reply) is lost → client blocks in recvfrom forever. UDP has no retransmission — the application must add timeouts (SIGALRM, select with a timeout, or SO_RCVTIMEO). Subtlety: a timeout alone can't tell whether the request or the reply was lost — an important distinction for operations that aren't idempotent (debiting an account twice ≠ reading a counter twice). Reliable-over-UDP needs sequence numbers + retransmission + (ideally) RTT estimation — at which point you have rebuilt half of TCP; know when that effort is worth it.

Verifying the reply's source: recvfrom accepts datagrams from anyone; a careful client compares the reply's source address with the server's (or simply connects — below).

2.3 No flow control — UDP's foot-gun

Blast 2000 large datagrams at a slow receiver and watch most vanish: the receiving socket buffer fills, the kernel silently drops the excess. No backpressure exists. Demonstration numbers in the classic experiment: 2000 sent, ~30 received. Cures: application-level pacing/acks, bigger SO_RCVBUF (palliative), or just use TCP. Check drops with netstat -s -p udp ("receive buffer errors").

2.4 connect() on a UDP socket — the connected UDP socket

connect on UDP sends nothing — it merely records the peer address in the kernel:

AspectUnconnected UDPConnected UDP
sendsendto(..., to, len)plain write/send
receivefrom anyoneonly from the connected peer
async errors (ICMP port unreachable)not delivereddelivered → ECONNREFUSED on next call
repeated sends to same peerroute looked up each timecached — measurably faster

The famous asymmetry explained: send to a dead port from an unconnected socket and the ICMP error has no associated socket to report to — your client just times out; a connected socket gets ECONNREFUSED. Always connect a request-reply UDP client.

2.5 Determining the outgoing interface

On a multihomed host, which local IP will a datagram leave from? A connected UDP socket answers without sending a byte:

sock = socket(AF_INET, SOCK_DGRAM, 0);
connect(sock, (struct sockaddr *)&dest, sizeof(dest)); /* no packets sent  */
getsockname(sock, (struct sockaddr *)&local, &len);    /* kernel's chosen  */
printf("would leave from %s\n", inet_ntop(...));       /* source IP+port  */

The kernel runs its routing decision at connect time and getsockname reads the result — a standard trick (and lab exercise) for "what's my outbound IP toward host X?".

2.6 TCP vs UDP server structure — summary table

FeatureTCP serverUDP server
socketslistening + one per clientone
callsbind, listen, accept, read/writebind, recvfrom/sendto
concurrencyfork / threads / selectusually iterative
client identityimplicit in connected socketfrom-address of each datagram
connection state in kernelyesnone

2.7 recvfrom and sendto, argument by argument

The two prototypes deserve the same per-argument treatment the TCP calls got:

  • fd — a SOCK_DGRAM socket (bound for servers; binding is optional for clients — the first sendto triggers an implicit bind to an ephemeral port).
  • buf, n — the datagram buffer. Crucial UDP semantics: exactly one datagram per call, and if the datagram is larger than n, the excess is silently truncated and lost (unlike TCP, where the rest waits for the next read). Always size buffers to the largest expected datagram.
  • flags — same flag set as recv/send (MSG_PEEK, MSG_DONTWAIT...).
  • from/fromlen (recvfrom) — out: who sent this datagram; the value-result length again. Pass NULL/NULL if you don't care.
  • to/tolen (sendto) — in: the destination of this one datagram.

And the matching client loop, for completeness next to dg_echo:

void dg_cli(FILE *fp, int sockfd, const struct sockaddr *servaddr, socklen_t servlen) {
    int n; char sendline[MAXLINE], recvline[MAXLINE + 1];
    while (fgets(sendline, MAXLINE, fp) != NULL) {
        sendto(sockfd, sendline, strlen(sendline), 0, servaddr, servlen);
        n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);  /* trusting! */
        recvline[n] = 0;
        fputs(recvline, stdout);
    }
}

The two marked flaws — it never verifies the reply's source, and it blocks forever on loss — are exactly what §2.2 and the connected-socket discussion fix; exam questions often hand you this code and ask for the critique.

2.8 Adding reliability over UDP — the checklist

When the question asks "how would you make a UDP application reliable?", enumerate the machinery (each item maps to something TCP gives free):

  1. Sequence numbers in each request, echoed in the reply — detects duplicates, matches replies to requests.
  2. Timeout and retransmission of unanswered requests.
  3. Adaptive RTT estimation — fixed timeouts are wrong on every network but one; keep a smoothed RTT and variance (exactly TCP's RTO algorithm) per peer.
  4. Karn's rule — never update RTT estimates from a retransmitted exchange's timing (you can't tell which transmission the reply answers); back the timer off exponentially instead.
  5. Idempotence or a duplicate cache — a retransmitted request may execute twice at the server unless requests are idempotent or the server caches recent (client, seq) → reply.

Conclude with the judgement sentence: for one-shot request/response, this is a few dozen lines and worth it (DNS does it); for anything stream-like, you are reimplementing TCP badly — switch protocols.

2.9 Worked failure: UDP datagram to a closed port

Mirror of TCP's Case B, and a favourite compare-question. Client sendto a port nobody owns:

Why the asymmetry exists is worth two sentences: the ICMP error arrives carrying the offending datagram's headers; for a connected socket the kernel can match (peer addr, ports) to exactly one socket and post the error there. An unconnected socket may have sent to many destinations — the 4-tuple match fails, and the kernel (per the classic BSD behaviour) drops the error rather than guess. This is also the gap the icmpd daemon of Unit 4 closes.

Exam pointers

  • "Compare TCP and UDP socket programming" — the §2.6 table plus the ladder diagrams from both lessons; mention that UDP needs neither listen/accept nor fork.
  • "What is a connected UDP socket? What are its advantages?" — §2.4's four-row table; the async-error row and the always-connect-request-reply rule are the scoring lines.
  • "Explain the lost-datagram problem and its solution" — loss → infinite block → timeout; then the request-vs-reply ambiguity and idempotence (that second half is what separates grades).
  • Numeric bait: a 0-return from recvfrom is a zero-length datagram, not EOF — there is no EOF on UDP.

Check yourself

  1. A 2000-byte datagram is received with recvfrom(buf, 1024). What does the application get, and where did the rest go?
  2. Why does an unconnected UDP client never see ECONNREFUSED?
  3. Your UDP query client occasionally debits a customer twice. Which reliability ingredient is missing — and which two designs fix it?
  4. How can a program discover its outgoing interface toward 8.8.8.8 without sending a packet? Name all three calls involved.
  5. 2000 datagrams sent on a LAN, 1970 lost, no congestion. Explain the mechanism and name the netstat counter that proves it.