Back to Blog

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.

2021-03-09
Share
Computer Engineeringnetworkingtcp-ip

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
  • DNS (Domain Name System): a hierarchical, distributed naming system that translates human-readable domain names (like example.com) into IP addresses
  • TTL (Time To Live): a field in DNS records and IP packets that limits how long the data is considered valid; in DNS, it controls how long a resolver caches a record
  • Latency: the time delay between sending a request and receiving a response
  • Bandwidth: the maximum rate at which data can be transferred over a network link, measured in bits per second
  • MSS (Maximum Segment Size): the largest amount of data (in bytes) that TCP will send in a single segment, negotiated during the handshake
  • Congestion window (cwnd): a TCP sender-side variable that limits the amount of unacknowledged data in flight, adjusted by congestion control algorithms
  • Receive window (rwnd): the amount of buffer space the receiver has available, advertised to the sender for flow control
  • 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:

    Application Layer HTTP, DNS, SMTP, SSH, FTP Transport Layer TCP, UDP Internet Layer IP, ICMP, ARP Link Layer Ethernet, Wi-Fi, PPP L4 L3 L2 L1 Each layer adds its own header (encapsulation)

    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):

    1. Client sends SYN with an initial sequence number
    2. Server responds with SYN-ACK, acknowledging the client's sequence number and providing its own
    3. 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":

    1. Check the local DNS cache (browser cache, OS cache)
    2. If not cached, query the configured recursive resolver (usually provided by the ISP or a service like 8.8.8.8)
    3. 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
    4. 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:

    $\text{Throughput} \leq \frac{\text{cwnd}}{\text{RTT}}$

    During slow start, cwnd doubles every RTT. After $k$ RTTs:

    $\text{cwnd}(k) = \text{MSS} \times 2^k$

    Time to reach a cwnd of $W$ bytes from slow start:

    $t = \text{RTT} \times \log_2\left(\frac{W}{\text{MSS}}\right)$

    Bandwidth-delay product (BDP) determines the optimal window size for full link utilization:

    $\text{BDP} = \text{Bandwidth} \times \text{RTT}$

    For a 1 Gbps link with 50 ms RTT:

    $\text{BDP} = 10^9 \times 0.05 = 50 \times 10^6 \text{ bits} = 6.25 \text{ MB}$

    The sender needs a cwnd of at least 6.25 MB to fully utilize this link.

    DNS resolution latency (worst case, no caching):

    $t_{\text{DNS}} = t_{\text{root}} + t_{\text{TLD}} + t_{\text{auth}} \approx 3 \times \text{RTT}_{\text{avg}}$

    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)