Search Tech Journey

Find topics, journeys and posts

6-month learning plan61 / 130
back to blog
systemsintermediate 15m read

S061 · Networking I — TCP/IP, DNS, Sockets

How bytes cross the internet.

Module M07: Systems & Infrastructure · Session 61 of 130 · Track: SYS 📡

What you'll be able to do after this session

  • Explain Networking I to a friend in one minute, out loud, no notes.
  • Recognise it when you see it in real code / systems / papers.
  • Complete the hands-on exercise at the end without looking up help.

Prerequisites

If you can't answer these in 30 seconds each, redo the linked session first:


Pre-read (skim before the session)


(a) Intuition · 5 min

The one-paragraph mental model. Why this concept exists, what problem it solves, and the analogy that makes it stick.

In one sentence: How bytes cross the internet.

Think about mailing a physical letter across the country. You write the message, put it in an envelope, address it, drop it in a postbox, and trust an invisible chain of trucks, planes, and sorting centres to hand it to the right person. The internet works exactly the same way — except the letter is a few thousand bytes, the envelope is a "packet", the address is an IP number, and the sorting centres are routers. The genius of the design is that no single machine knows the whole route; each router just knows "for that destination, go this way", and packets hop from one to the next until they arrive.

IP (Internet Protocol) is the addressing + routing layer — it delivers packets, but doesn't promise they arrive in order or at all. TCP (Transmission Control Protocol) sits on top and adds the guarantees: "these bytes will arrive, in order, exactly once, or you'll get an error". It does this with sequence numbers, ACKs, and retransmissions — like registered mail with tracking. DNS (Domain Name System) is the phonebook: you type google.com, DNS translates it to 142.250.192.14 (an IP address) so IP knows where to route.

A socket is your program's handle on this whole machinery. When you open("http://x.com"), under the hood Python opens a TCP socket to port 80, which triggers a 3-way handshake (SYN → SYN/ACK → ACK), which sends packets through your ISP's routers to the destination server, whose kernel hands them to a listening process.

Forever-gotcha: TCP is a byte stream, not a message stream. If you send("hello") then send("world"), the receiver may recv() and get "helloworld", or "hell" then "oworld". There are no message boundaries — you must add them yourself (length prefix, delimiter, or a framing protocol like HTTP).


(b) Visual walkthrough · 15 min

Diagrams, worked examples, step-by-step visuals.

Here's what happens when you type curl https://example.com and hit Enter:

Layer cake (TCP/IP model — the one that's actually used):

LayerJobExample
ApplicationWhat you speakHTTP, SMTP, SSH, DNS
TransportReliable / unreliable byte channelTCP (reliable), UDP (fire-and-forget)
InternetRouting packets across networksIP (v4 = 32-bit, v6 = 128-bit)
LinkActual wire / wifi frameEthernet, Wi-Fi 802.11

Worked example — the 3-way handshake:

  1. Client picks random seq = 1000, sends SYN(seq=1000).
  2. Server picks random seq = 5000, replies SYN(seq=5000), ACK(ack=1001) — meaning "I got up to 1000, next expected is 1001".
  3. Client replies ACK(ack=5001). Now both sides agree on starting sequence numbers and the connection is established.

Worked example — DNS resolution for blog.example.com:

  1. Your machine asks your resolver (e.g. 1.1.1.1 or your ISP's).
  2. Resolver asks a root server: "who handles .com?" → gets pointer to a TLD server.
  3. Resolver asks the TLD server: "who handles example.com?" → gets pointer to ns1.example.com (authoritative).
  4. Resolver asks the authoritative: "what's blog.example.com?" → gets A 93.184.216.34.
  5. Resolver caches the answer for the TTL (say 300s) and returns it to you.

Every subsequent lookup within TTL is served from cache — which is why DNS changes take time to propagate.


(c) Hands-on · 20 min

Code you type and run. Not pseudo-code — real, runnable, copy-pasteable.

Below: a minimal TCP echo server + client in pure Python (no frameworks). Save as echo.py.

# echo.py — run server in one terminal, client in another
import socket, sys, threading
 
HOST, PORT = "127.0.0.1", 9999
 
def server():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind((HOST, PORT))
    s.listen(5)
    print(f"[server] listening on {HOST}:{PORT}")
    while True:
        conn, addr = s.accept()
        print(f"[server] connected by {addr}")
        threading.Thread(target=handle, args=(conn,), daemon=True).start()
 
def handle(conn):
    with conn:
        while True:
            data = conn.recv(1024)      # blocks until bytes arrive
            if not data:                 # empty = peer closed
                print("[server] client hung up")
                return
            print(f"[server] recv {len(data)} bytes: {data!r}")
            conn.sendall(b"echo: " + data)
 
def client(msg: str):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((HOST, PORT))
        s.sendall(msg.encode())
        reply = s.recv(4096)
        print(f"[client] got {reply!r}")
 
if __name__ == "__main__":
    if sys.argv[1] == "server":
        server()
    else:
        client(" ".join(sys.argv[2:]) or "hello")

Run it:

# terminal 1
python3 echo.py server
 
# terminal 2
python3 echo.py client hello world
python3 echo.py client "another message"

Checklist — what to observe:

  1. In terminal 1, watch connected by ('127.0.0.1', <port>). The client's port is random and different each run — that's the ephemeral port your OS picks.
  2. Try ss -tnp | grep 9999 (Linux) while the client is running. You'll see two rows: one LISTEN and one ESTAB.
  3. Kill the client with Ctrl-C during recv. Server prints "client hung up" — that's TCP's FIN travelling back.
  4. Try sending two sendall calls back-to-back in the client. On the server, recv(1024) may return both concatenated — proof that TCP is a byte stream, not a message stream.
  5. Run sudo tcpdump -i lo -X port 9999 in a third terminal and re-run the client. You'll literally see the SYN / SYN-ACK / ACK / PSH / FIN packets.

Try this modification: wrap the protocol with a 4-byte length prefix (struct.pack("!I", len(msg)) + msg) so the server can safely reassemble messages even when TCP splits or merges them. This is how real protocols (Kafka, gRPC framing) solve the byte-stream problem.


(d) Production reality · 10 min

How this shows up in real systems, gotchas, war stories, common bugs.

War story #1 — the 500ms mystery. A team noticed API latency was exactly 500ms extra on some requests. Turned out to be Nagle's algorithm interacting with delayed-ACK: the client sent a small header, then a small body, and TCP held the second write hoping to batch it. Fix: setsockopt(TCP_NODELAY, 1) for small-payload latency-sensitive protocols.

War story #2 — DNS TTL disaster. During a failover, an ops team lowered the DNS TTL from 3600 to 60 seconds after the incident started. Because the old TTL was still cached at millions of ISP resolvers, users kept hitting the dead server for the next hour. Rule: lower TTLs before planned failovers, not after.

War story #3 — connection limits. A Node service crashed with "too many open files". Each socket = one file descriptor; the default ulimit is 1024. Under high load with lots of long-lived connections you exhaust FDs. Real teams tune ulimit -n 65535 and use connection pools.

Common bugs & how top teams handle them:

  • Half-open connections. Client's wifi drops without sending FIN. Server keeps the socket forever until TCP keepalive (default 2h!) kicks in. Real services set SO_KEEPALIVE with 60s idle + application-level heartbeats.
  • TIME_WAIT flood. After close, the initiator holds the socket in TIME_WAIT for 2×MSL (~2 min). High-traffic proxies can run out of ephemeral ports. Fix with connection reuse (HTTP keep-alive), SO_REUSEADDR, and — carefully — net.ipv4.tcp_tw_reuse.
  • DNS as a SPOF. If your app calls getaddrinfo() on every request without caching, your DNS provider outage = your outage. Netflix and Facebook run internal caching resolvers + short-TTL health-based routing (via something like AWS Route 53 or their own).
  • Head-of-line blocking. In HTTP/1.1, one slow response blocks the pipeline behind it. HTTP/2 multiplexed streams; HTTP/3 went further and moved to UDP (QUIC) precisely to escape TCP's head-of-line blocking at the transport layer too.

Modern cloud shops (Cloudflare, Google) run at such scale that a single misconfigured MTU or a single BGP route leak becomes global news. If you take one thing away: the network is not reliable, ordered, or fast — TCP just does a good enough job hiding that from you most of the time.


(e) Quiz + exercise · 10 min

  1. Explain the 3-way handshake in 3 sentences, naming each packet type and what fields matter.
  2. Why is TCP called a "byte stream" and what practical bug does that create when writing your own protocol?
  3. What does DNS TTL control, and why does lowering it after an outage help nobody?
  4. Name two problems SO_KEEPALIVE solves and one it doesn't.
  5. When would you pick UDP over TCP? Give one real example (protocol + why).

Stretch problem (connects to S060 — OS Basics): every TCP socket consumes a file descriptor. Your server runs on a Linux box with ulimit -n = 1024. Assuming each request opens 1 socket + 2 log files + 1 DB connection, roughly how many concurrent requests can you serve before hitting the FD ceiling? What kernel-level change and what application-level change would you make to push that to 50 k concurrent connections? Sketch the answer in 5 bullets.


Explain-out-loud test

If you can't teach these 3 things to a friend without notes, redo the session:

  1. What is Networking I? (one sentence, no jargon)
  2. When would you use it? (one concrete example)
  3. When would you NOT use it? (one anti-pattern)

What comes next


Part of a 130-session evergreen learning series. Session structure: (a) intuition · (b) visual walkthrough · (c) hands-on · (d) production reality · (e) quiz + exercise. Duration: 60 minutes.

More from M07 · Systems & Infrastructure

all modules →
  1. 060OS Basics — Processes, Threads, Memory, FDs
  2. 062Networking II — Load Balancers L4 vs L7, Reverse Proxies
  3. 063Caching — Cache-Aside, Write-Through, TTLs, Invalidation
  4. 064CDN — Edge, Cache Hierarchies, Cache-Control
  5. 065Docker — Images, Layers, Dockerfile, Networking
  6. 066Kubernetes I — Pods, Deployments, Services