Networking Fundamentals: TCP/IP, UDP, and DNS
How data travels across networks using the TCP/IP stack, the trade-offs between TCP and UDP, how DNS resolves domain names, and the basics of socket programming.
Terminology
- Protocol: a set of rules that defines how data is formatted, transmitted, and received between networked devices
- TCP (Transmission Control Protocol): a connection-oriented, reliable transport protocol that guarantees in-order delivery of data using acknowledgments, retransmissions, and flow control
- UDP (User Datagram Protocol): a connectionless, unreliable transport protocol that sends datagrams without guarantees of delivery, ordering, or duplicate protection
- IP (Internet Protocol): the network-layer protocol responsible for addressing and routing packets between hosts across networks
- Packet: a unit of data transmitted over a network, consisting of a header (metadata like source/destination addresses) and a payload (the actual data)
- Socket: an endpoint for network communication, identified by an IP address and port number; the programming interface for sending and receiving data over a network
- Port: a 16-bit number (0-65535) that identifies a specific process or service on a host; allows multiple network applications to run on the same machine
- Three-way handshake: the TCP connection establishment process consisting of SYN, SYN-ACK, and ACK messages between client and server
- Acknowledgment (ACK): a message sent by the receiver to confirm that data has been received successfully
- Sequence number: a number assigned to each byte of data in a TCP stream, used to ensure correct ordering and detect missing data
- RTT (Round-Trip Time): the time it takes for a packet to travel from sender to receiver and for the acknowledgment to return
- Congestion control: mechanisms in TCP that adjust the sending rate to avoid overwhelming the network; includes slow start, congestion avoidance, fast retransmit, and fast recovery
- Flow control: a mechanism where the receiver advertises how much data it can accept (receive window), preventing the sender from overwhelming the receiver's buffer
What & Why
Every time you load a web page, send a message, or stream a video, data travels through a layered stack of network protocols. At the bottom, the physical and link layers handle transmitting bits over wires, fiber, or radio. Above that, the Internet Protocol (IP) routes packets between machines across the globe. At the transport layer, TCP and UDP provide different guarantees about how that data is delivered to applications.
TCP is the workhorse of the internet. It provides reliable, ordered delivery: if you send bytes in a certain order, they arrive in that order, and if any are lost, TCP retransmits them. This reliability comes at a cost: connection setup (the three-way handshake), acknowledgment overhead, and congestion control that throttles the sender when the network is busy. HTTP, SSH, email, and most web traffic use TCP.
UDP is the lightweight alternative. It sends datagrams with no connection setup, no acknowledgments, and no ordering guarantees. If a packet is lost, UDP does not retransmit it. This makes UDP faster and simpler for applications that can tolerate some loss, like video streaming, online gaming, and DNS queries.
DNS is the internet's phone book. Before your browser can connect to a server, it needs to translate the domain name into an IP address. DNS is a distributed, hierarchical database that handles billions of lookups per day with remarkable efficiency through aggressive caching.
Understanding these protocols is fundamental for any software engineer. Debugging network issues, optimizing application performance, designing distributed systems, and implementing secure communication all require knowledge of how TCP/IP, UDP, and DNS work.
How It Works
The TCP/IP Layer Model
The TCP/IP model organizes network communication into four layers, each with a specific responsibility:
When an application sends data, each layer wraps it with its own header. The application layer produces the message. TCP adds a segment header (ports, sequence numbers). IP adds a packet header (source/destination IP). The link layer adds a frame header (MAC addresses). At the receiver, each layer strips its header and passes the payload up.
TCP: Reliable, Ordered Delivery
Connection establishment (three-way handshake):
- Client sends SYN with an initial sequence number
- Server responds with SYN-ACK, acknowledging the client's sequence number and providing its own
- Client sends ACK, confirming the server's sequence number
After the handshake, both sides can send data. The handshake costs one full RTT before any data flows.
Reliable delivery: TCP assigns a sequence number to every byte. The receiver sends ACKs indicating the next expected byte. If the sender does not receive an ACK within a timeout, it retransmits the data. This guarantees that all data arrives, even if packets are lost.
Flow control: The receiver advertises its receive window (rwnd) in every ACK, telling the sender how much buffer space is available. The sender never sends more unacknowledged data than rwnd allows, preventing receiver buffer overflow.
Congestion control: TCP uses algorithms to detect and respond to network congestion:
- Slow start: Begin with a small congestion window (cwnd = 1 MSS) and double it every RTT until a threshold is reached
- Congestion avoidance: After the threshold, increase cwnd by 1 MSS per RTT (linear growth)
- Fast retransmit: If three duplicate ACKs are received, retransmit the missing segment immediately without waiting for a timeout
- Fast recovery: After fast retransmit, halve cwnd and enter congestion avoidance (do not restart from slow start)
Connection teardown: Either side sends a FIN. The other side ACKs and sends its own FIN. The initiator ACKs the final FIN. This four-way handshake ensures both sides have finished sending.
UDP: Fast, Simple Datagrams
UDP adds minimal overhead: just source port, destination port, length, and checksum (8 bytes total). There is no connection setup, no sequence numbers, no acknowledgments, and no congestion control. Each UDP datagram is independent.
Applications that use UDP handle reliability themselves if needed. For example, DNS sends a query over UDP and retransmits if no response arrives within a timeout. Video streaming tolerates occasional lost frames rather than waiting for retransmissions that would cause visible delays.
DNS Resolution
When a client needs to resolve "www.example.com":
- Check the local DNS cache (browser cache, OS cache)
- If not cached, query the configured recursive resolver (usually provided by the ISP or a service like 8.8.8.8)
- The resolver checks its cache. If not found, it starts an iterative resolution: a. Query a root name server for ".com" b. The root server responds with the address of the ".com" TLD name server c. Query the ".com" TLD server for "example.com" d. The TLD server responds with the address of example.com's authoritative name server e. Query the authoritative server for "www.example.com" f. The authoritative server responds with the IP address
- The resolver caches the result (respecting the TTL) and returns it to the client
Each DNS record has a TTL that controls how long it can be cached. Short TTLs (60 seconds) allow fast failover but increase DNS traffic. Long TTLs (86400 seconds) reduce traffic but delay propagation of changes.
Complexity Analysis
| Metric | TCP | UDP |
|---|---|---|
| Connection setup | $1.5 \times \text{RTT}$ (3-way handshake) | $0$ (connectionless) |
| Header overhead | $20$-$60$ bytes | $8$ bytes |
| Reliability | Guaranteed (retransmission) | None (best effort) |
| Ordering | Guaranteed (sequence numbers) | None |
TCP throughput is bounded by the congestion window and RTT:
During slow start, cwnd doubles every RTT. After $k$ RTTs:
Time to reach a cwnd of $W$ bytes from slow start:
Bandwidth-delay product (BDP) determines the optimal window size for full link utilization:
For a 1 Gbps link with 50 ms RTT:
The sender needs a cwnd of at least 6.25 MB to fully utilize this link.
DNS resolution latency (worst case, no caching):
With caching, most lookups resolve in a single RTT to the recursive resolver or from the local cache (0 ms).
Implementation
ALGORITHM TCPThreeWayHandshake(client, server)
INPUT: client: initiating host, server: listening host
OUTPUT: established TCP connection
BEGIN
// Step 1: Client sends SYN
clientISN <- GENERATE_INITIAL_SEQUENCE_NUMBER()
synPacket <- CREATE_PACKET(
flags: SYN,
seqNum: clientISN,
srcPort: client.port,
dstPort: server.port
)
SEND(client, server, synPacket)
client.state <- SYN_SENT
// Step 2: Server receives SYN, sends SYN-ACK
serverISN <- GENERATE_INITIAL_SEQUENCE_NUMBER()
synAckPacket <- CREATE_PACKET(
flags: SYN + ACK,
seqNum: serverISN,
ackNum: clientISN + 1,
srcPort: server.port,
dstPort: client.port
)
SEND(server, client, synAckPacket)
server.state <- SYN_RECEIVED
// Step 3: Client receives SYN-ACK, sends ACK
ackPacket <- CREATE_PACKET(
flags: ACK,
seqNum: clientISN + 1,
ackNum: serverISN + 1
)
SEND(client, server, ackPacket)
client.state <- ESTABLISHED
server.state <- ESTABLISHED
RETURN CONNECTION(client, server)
END
ALGORITHM TCPSlowStartAndCongestionAvoidance(connection)
INPUT: connection: established TCP connection
OUTPUT: data transmitted with congestion control
BEGIN
cwnd <- 1 * MSS
ssthresh <- 65535 // initial slow start threshold
dupAckCount <- 0
WHILE data to send DO
// Send up to cwnd bytes
bytesInFlight <- 0
WHILE bytesInFlight < MIN(cwnd, connection.rwnd) AND data remaining DO
segment <- GET_NEXT_SEGMENT(data, MSS)
SEND_SEGMENT(connection, segment)
bytesInFlight <- bytesInFlight + LENGTH(segment)
START_RETRANSMIT_TIMER(segment)
END WHILE
// Wait for ACKs
event <- WAIT_FOR_EVENT()
IF event.type = NEW_ACK THEN
dupAckCount <- 0
IF cwnd < ssthresh THEN
// Slow start: exponential growth
cwnd <- cwnd + MSS
ELSE
// Congestion avoidance: linear growth
cwnd <- cwnd + MSS * (MSS / cwnd)
END IF
ELSE IF event.type = DUPLICATE_ACK THEN
dupAckCount <- dupAckCount + 1
IF dupAckCount = 3 THEN
// Fast retransmit
ssthresh <- MAX(cwnd / 2, 2 * MSS)
RETRANSMIT(event.missingSegment)
cwnd <- ssthresh + 3 * MSS // fast recovery
END IF
ELSE IF event.type = TIMEOUT THEN
ssthresh <- MAX(cwnd / 2, 2 * MSS)
cwnd <- 1 * MSS // restart slow start
RETRANSMIT(event.timedOutSegment)
END IF
END WHILE
END
ALGORITHM DNSResolveIterative(domain, resolver)
INPUT: domain: fully qualified domain name, resolver: recursive resolver
OUTPUT: IP address or error
BEGIN
// Check resolver cache
cached <- CACHE_LOOKUP(resolver.cache, domain)
IF cached is not NULL AND cached.ttl > CURRENT_TIME() THEN
RETURN cached.address
END IF
// Split domain into labels
labels <- SPLIT(domain, ".") // e.g., ["www", "example", "com"]
// Start from root
currentServer <- ROOT_NAME_SERVER
// Iterate from TLD down to the target
FOR i <- LENGTH(labels) - 1 DOWNTO 0 DO
queryName <- JOIN(labels[0..i], ".")
response <- SEND_DNS_QUERY(currentServer, queryName, TYPE_A)
IF response.type = ANSWER THEN
// Found the final answer
CACHE_INSERT(resolver.cache, domain, response.address, response.ttl)
RETURN response.address
ELSE IF response.type = REFERRAL THEN
// Got a referral to a more specific name server
currentServer <- response.nameServer
ELSE IF response.type = NXDOMAIN THEN
RETURN ERROR("domain does not exist")
END IF
END FOR
RETURN ERROR("resolution failed")
END
ALGORITHM UDPSendReceive(socket, destAddr, destPort, data)
INPUT: socket: UDP socket, destAddr: destination IP, destPort: destination port, data: payload
OUTPUT: sent datagram
BEGIN
// Construct UDP datagram
datagram <- CREATE_DATAGRAM(
srcPort: socket.localPort,
dstPort: destPort,
length: 8 + LENGTH(data), // 8-byte header + payload
checksum: COMPUTE_CHECKSUM(data),
payload: data
)
// Hand to IP layer (no connection setup needed)
ipPacket <- CREATE_IP_PACKET(
srcAddr: socket.localAddr,
dstAddr: destAddr,
protocol: UDP,
payload: datagram
)
SEND_TO_NETWORK(ipPacket)
// No acknowledgment expected, no retransmission
RETURN SUCCESS
END
Real-World Applications
- Web browsing: every HTTP request runs over TCP, which guarantees that HTML, CSS, and JavaScript arrive completely and in order; HTTP/1.1 uses persistent TCP connections to avoid repeated handshakes
- Video streaming: services like Netflix use TCP for adaptive bitrate streaming (DASH/HLS) where reliability matters more than latency; live streaming and video conferencing often use UDP (via WebRTC) where low latency matters more than perfect delivery
- Online gaming: multiplayer games use UDP for real-time state updates (player positions, actions) because a late packet is worse than a lost one; game state is updated so frequently that retransmitting old data is pointless
- DNS infrastructure: DNS queries typically use UDP for speed (single request-response), but fall back to TCP for responses larger than 512 bytes or for zone transfers between DNS servers
- CDN edge servers: content delivery networks optimize TCP parameters (initial cwnd, congestion control algorithm) at edge locations close to users to minimize the impact of RTT on throughput
- Database replication: distributed databases use TCP for replication traffic to ensure all writes are reliably delivered to replicas; some use custom protocols over TCP with application-level acknowledgments for stronger guarantees
Key Takeaways
- TCP provides reliable, ordered delivery through sequence numbers, acknowledgments, and retransmissions, at the cost of connection setup latency ($1.5 \times \text{RTT}$) and congestion control overhead
- UDP provides minimal overhead (8-byte header) and no connection setup, making it ideal for latency-sensitive applications that can tolerate packet loss
- TCP congestion control (slow start, congestion avoidance) adapts the sending rate to network conditions; throughput is bounded by $\frac{\text{cwnd}}{\text{RTT}}$
- The bandwidth-delay product ($\text{BDP} = \text{Bandwidth} \times \text{RTT}$) determines the optimal TCP window size for full link utilization
- DNS translates domain names to IP addresses through a hierarchical resolution process; aggressive caching (controlled by TTL) makes most lookups fast
- The choice between TCP and UDP depends on whether your application needs reliability (use TCP) or low latency with tolerance for loss (use UDP)