Search Tech Journey

Find topics, journeys and posts

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

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

How bytes actually cross the internet — from getaddrinfo() to the 3-way handshake to the router hop that decides your latency. The mental model every backend engineer needs before they can debug ‘why is this slow?’

⚙️SystemsM07 · Systems & Infrastructure· Session 061 of 130 60 min

🎯 Walk through a curl request end-to-end — DNS lookup, TCP handshake, packet routing, socket buffers — and be able to point at every layer where it can break.

Why this session exists

Every backend engineer eventually gets paged for a system that's "up" but nobody can reach it. The DB says green, the load balancer says green, the app process is running — and yet requests time out. Nine times out of ten the bug is somewhere in the seven layers below your code, in a place your framework has been quietly hiding from you. This session peels those layers open so the next time you see EHOSTUNREACH, ETIMEDOUT, or a 500 ms latency spike, you know exactly which layer to look at first.

You will be able to
  • Walk through what happens between typing curl example.com and getting HTML back — DNS, TCP handshake, TLS, HTTP, FIN — naming every packet.
  • Explain why TCP is a byte stream (not a message stream), and design a length-prefixed framing protocol that handles the resulting bug.
  • Read the output of ss -tnp, tcpdump, and dig well enough to diagnose which layer a failure lives in.
  • Recognise the top-5 production networking gotchas (TIME_WAIT flood, half-open connections, DNS TTL, Nagle+delayed-ACK, FD ceiling) in under 30 seconds.
  • Write a working TCP echo server + client from memory in under 20 minutes.

Prerequisites



(a) Intuition · 5 min

The internet is the world's largest postal system
🌍 Real world

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. Nobody in the middle knows the whole journey; each sorting centre just knows "for that ZIP code, throw it on this truck."

If the letter is important, you pay for registered mail: someone signs for it, you get a tracking number, and if the postal service loses it they'll retry. If you're just sending a birthday card, you drop it in the box and hope.

💻 Code world

The internet works the same way. IP is the postbox + sorting-centre network — it delivers packets to an address, but never promises they arrive, in order, or only once. TCP is registered mail sitting on top of IP: sequence numbers = tracking, ACKs = signatures, retransmissions = the second delivery attempt. UDP is the birthday card — just send it and move on. DNS is the phonebook that turns google.com into 142.250.192.14 so IP knows which "ZIP code" to route to.

A socket is your program's grip on that whole machinery — a file descriptor the kernel gives you, that you can read() and write() just like a file, except the bytes travel through six intermediate networks to reach the other side.

The four ideas that unlock everything else

If you internalise only these, 90% of production networking makes sense
  • The network is not reliable, not ordered, not fast, and not private. TCP + TLS just do a good enough job hiding that from you most of the time.
  • Every socket is a file descriptor. Everything you learned about FDs (ulimit, epoll, close-on-exec) applies. The FD table is the same table.
  • TCP is a byte stream, not a message stream. If you send(‘hello’) then send(‘world’), the receiver may recv(‘helloworld’) or recv(‘hell’) then recv(‘oworld’). You must add message boundaries yourself.
  • DNS is a distributed, cached, TTL-driven database — not a live query. Anything you change is stale somewhere for at least the TTL.

A quick history so you know why the world looks like this

  1. 1969
    ARPANET · first packet-switched network
    Four US universities connected via IMPs (Interface Message Processors). ‘LOGIN’ is the first message; it crashes after ‘LO’.
  2. 1974
    TCP/IP proposed · Cerf & Kahn
    ‘A Protocol for Packet Network Intercommunication’ — the paper that becomes RFC 675, then the internet.
  3. 1983
    ARPANET flag day → TCP/IP
    The entire network cuts over from NCP to TCP/IP on Jan 1. Every host had to upgrade. Never done again.
  4. 1983
    DNS invented · Paul Mockapetris
    Replaces the single hosts.txt file that everyone was maintaining by hand. RFC 882/883.
  5. 1994
    SSL 1.0 → 3.0 · Netscape
    TLS's ancestor. First serious attempt to encrypt TCP for e-commerce.
  6. 2015
    HTTP/2 goes RFC (7540)
    Multiplexed streams over one TCP connection. Fixes app-layer head-of-line blocking.
  7. 2022
    HTTP/3 goes RFC (9114)
    Runs over QUIC (over UDP). Escapes TCP's transport-layer head-of-line blocking entirely.

(b) Visual walkthrough · 15 min

What actually happens when you type curl https://example.com

That single curl invocation touched every layer of the TCP/IP stack. If any one of them misbehaves, the request fails — but the error message you see (curl: (7) Failed to connect) tells you nothing about which one. That's what the rest of this session teaches you to disambiguate.

The layer cake — the model you actually use

TCP/IP stack (top = your code, bottom = electrons on a wire)

L7 · Application
What your program speaks. HTTP, gRPC, SMTP, SSH, DNS. Framed messages, semantics, retries.
your code
L4 · Transport (TCP or UDP)
TCP = reliable, ordered byte stream, congestion controlled. UDP = fire-and-forget datagrams. This is where SYN/ACK/FIN live.
kernel
L3 · Internet (IP)
Best-effort delivery of packets to an IP address. Routers make hop-by-hop forwarding decisions via BGP.
kernel + routers
L2 · Link
Ethernet / Wi-Fi frames on the local segment. MAC addresses, ARP, switches.
NIC + switch
L1 · Physical
Actual electrons in copper, photons in fibre, radio in 2.4/5/6 GHz air.
hardware

The 3-way handshake, spelled out

1client → server
SYN

Client picks a random seq number (say 1000) and sends SYN(seq=1000). This defends against ancient stray packets from a previous connection.

2server → client
SYN/ACK

Server picks its own random seq (say 5000) and replies SYN(seq=5000), ACK(ack=1001). ‘I got up to 1000, next expected is 1001.’

3client → server
ACK

Client replies ACK(ack=5001). Both sides now agree on starting sequence numbers.

41 RTT total
ESTABLISHED

The kernel marks the socket ESTABLISHED. Your app's connect() call returns. First byte of data can flow.

How DNS actually resolves blog.example.com

The resolver caches every answer for the TTL. Every subsequent lookup within TTL is served from cache — which is why DNS changes take time to propagate and why lowering a TTL after an outage starts is too late.

TCP vs UDP — when to pick which

TCP

Reliable, ordered, congestion-controlled byte stream

  • 3-way handshake before any data (1 RTT of latency tax)
  • Guaranteed delivery via ACKs + retransmissions
  • In-order delivery — recv() gives bytes in send order
  • Congestion control (slow start, cubic) — plays nice with the network
  • Use for: HTTP/1.1+2, gRPC, SSH, SMTP, databases, anything correctness-sensitive
UDP

Fire-and-forget datagrams — you build the reliability you need

  • No handshake, no state — first packet is the message
  • No delivery guarantee, no ordering, no retransmission
  • Message-oriented — one send = one recv (or drop)
  • You get to invent the semantics: DNS did, QUIC did, gaming/voice do
  • Use for: DNS, DHCP, NTP, video/voice, gaming, custom protocols (QUIC, WireGuard)

The port number picture

0–1023
Well-known ports
22 SSH, 53 DNS, 80 HTTP, 443 HTTPS. Root-only to bind on Linux.
1024–49151
Registered ports
5432 Postgres, 6379 Redis, 9092 Kafka. Anyone can bind.
49152–65535
Ephemeral ports
Your OS picks one from this range for every outbound connection.
~28k
Practical ephemeral ceiling
Default Linux range. Blown by a busy egress proxy — see TIME_WAIT gotcha.

Common misconception
✗ What most people think

"TCP guarantees delivery. If send() returns successfully, the data reached the other side."

✓ What is actually true

A successful send() means the bytes were copied into the kernel's socket send buffer. Nothing more. They may not have left the machine, and the peer may already be gone. TCP guarantees that data is delivered in order and without gaps, or the connection fails — it cannot guarantee delivery across a network that has stopped forwarding packets, and it certainly cannot tell you the application on the other end processed anything.

Why the myth is so sticky

The myth is sticky because in a healthy datacenter it is effectively true a very high proportion of the time, so it survives every test you run. It breaks exactly when it matters: during a partition or a peer crash, the sender happily writes into its buffer for as long as the window allows, and the failure surfaces seconds or minutes later — or never, if no keepalive is configured. This is why a client can appear connected to a server that was terminated ten minutes ago.

Prove it to yourself

Watch data sit in the kernel with the application none the wiser:

ss -tin
# Send-Q shows bytes accepted by the kernel but NOT yet acknowledged.
# A large, non-draining Send-Q means the peer is gone or stalled,
# while your application's send() calls kept returning success.

ss -tan | awk '{print $1}' | sort | uniq -c
# count sockets per state - a pile of CLOSE-WAIT means YOUR app
# is not calling close() on connections the peer already closed.
From first principles
Start with the question

Why does TCP deliberately slow itself down at the start of every connection instead of sending at full speed immediately?

  1. 1
    The network between two endpoints is shared by every other connection, and no endpoint knows its capacity or how many others are using it.
    forced by · there is no signalling channel telling a sender the available bandwidth on a path it does not control
  2. 2
    Routers have finite buffers. When arrival rate exceeds forwarding rate, buffers fill and packets are dropped.
    forced by · a queue with a fixed bound must discard once full; there is nowhere else for the packet to go
  3. 3
    If every sender transmitted at full speed on connect, aggregate demand would exceed capacity, buffers would overflow, and mass loss would trigger mass retransmission — which adds yet more load.
    forced by · retransmission under congestion is positive feedback, and this is precisely how the 1986 Internet congestion collapse happened
  4. 4
    Therefore each sender must independently estimate its fair share, and the only observable feedback available is whether packets are being acknowledged.
    forced by · the network provides no explicit rate signal by default; loss and delay are the only universally available signals
  5. 5
    So a sender starts conservatively and increases its window while acknowledgements keep arriving, backing off sharply when loss indicates it exceeded capacity.
    forced by · probing upward is the only safe way to discover a limit you cannot query, and backing off hard is required for the system to converge rather than oscillate
⇒ Therefore

Therefore slow start is not conservatism, it is a distributed capacity-discovery algorithm running with no coordination between participants — and it is why the internet remains stable under load without any central controller.

And note what this predicts: short connections never reach full speed, because they finish while still ramping up. That single prediction explains why connection reuse and keep-alive matter so much, why HTTP/2's single multiplexed connection outperforms six parallel HTTP/1.1 connections, and why connection pooling is one of the highest-leverage changes available to a chatty client. It also predicts that on a high-bandwidth, high-latency path, the ramp costs many round trips — which is exactly the regime where TCP performs worst and where protocols like QUIC focused their effort.

Mental modelLayers of envelopes, each ignorant of the next

Your bytes go into a TCP envelope (ports, sequence numbers), that into an IP envelope (source and destination addresses), that into an Ethernet frame (MAC addresses, hop by hop). Each layer only knows its own addressing scheme and treats the layer above as opaque payload.

DNS sits outside this entirely: it is a lookup that happens before any of it, translating a name into the address the IP layer needs. It is a distributed cache with TTLs, and almost every DNS surprise is a caching surprise.

  • IP addresses route between networks; MAC addresses route within one. A packet keeps the same IP endpoints end to end while its Ethernet frame is rewritten at every hop — which is why traceroute works and why NAT is a violation worth understanding.
  • The three-way handshake costs one full round trip before any data flows, and TLS costs one or two more. On a 100 ms path that is 200–300 ms before the first byte of your request is sent, which is why connection reuse dominates latency for small requests.
  • DNS TTLs are advisory and honoured inconsistently by resolvers, OS caches, JVMs and libraries. Planning a cutover around a TTL means planning around the least compliant cache in the chain, so always keep the old endpoint alive well past the TTL.
  • Bandwidth and latency are independent, and you cannot fix latency with money. Throughput on a single TCP stream is bounded by window size divided by round-trip time — which is why a fat, long link stays slow until window scaling is right, and why cross-region transfers need parallel streams.
🔔 Fires when you see

Fire this model when you see: "the network is slow" with no measurement · a service reachable by IP but not by name · a deploy that half-worked after a DNS change · sockets stuck in CLOSE-WAIT · a transfer that will not exceed a suspiciously round throughput number.

The tradeoff

How long should a client wait before deciding a request failed, and what should it do next?

Short timeout, aggressive retry
+ you gain fast recovery from a single unlucky slow node, so tail latency stays close to median for the caller. Users are not left staring at a spinner while one bad backend times out slowly.
− you pay during a real slowdown every client retries simultaneously, multiplying load on an already-struggling service and converting a latency problem into an outage. This is the classic retry storm, and it turns partial degradation into total failure.
pick when idempotent reads against a service with headroom, and only with jittered exponential backoff and a circuit breaker
Long timeout, no retry
+ you gain generates no amplification, so a struggling dependency is allowed to recover rather than being hammered. Simpler to reason about and impossible to get subtly wrong.
− you pay one slow dependency holds your connections and threads for the full timeout, so your own resource pool exhausts and the failure propagates upstream to your callers. You have absorbed their problem into your availability.
pick when non-idempotent writes without idempotency keys, where a duplicate is worse than a failure
Deadline propagation with a budget
+ you gain the caller sets an absolute deadline that travels with the request through every hop, so no service spends effort on work whose result is already too late to use, and the total time is bounded regardless of call depth.
− you pay requires every service in the chain to honour and forward the deadline, which is an organisation-wide commitment. Partial adoption gives you most of the complexity and little of the benefit.
pick when any request path more than two services deep — beyond that, per-hop timeouts cannot bound total latency at all
What a senior engineer actually does

Always set an explicit timeout — the default in most HTTP clients is infinite or minutes long, which is never what you want. Then bound retries by a budget rather than a count, add full jitter, and put a circuit breaker in front of dependencies you retry against.

The insight that matters: a retry is a load-multiplying decision made at the worst possible moment, when the dependency is already unhealthy. Treat retry capacity as a shared resource with a cap — a common approach is to allow retries only while they remain a small fraction of total outbound requests, so a healthy system retries freely and a struggling one automatically stops amplifying its own failure.


(c) Hands-on · 25 min

Build a minimal TCP echo server + client in pure Python (no frameworks), then use it to see the byte-stream problem with your own eyes. Save as echo.py and run in two terminals.

#!/usr/bin/env python3
"""echo.py — minimal TCP echo server + client.
 
Run server:   python3 echo.py server
Run client:   python3 echo.py client hello world
Framed mode:  python3 echo.py framed-client hello world
"""
import socket
import struct
import sys
import threading
 
HOST, PORT = "127.0.0.1", 9999
 
 
# ---------- server ----------
def server() -> None:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # SO_REUSEADDR: reclaim the port even if a previous socket is in TIME_WAIT.
    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: socket.socket) -> None:
    """Naive handler — proves the byte-stream problem exists."""
    with conn:
        while True:
            data = conn.recv(1024)      # blocks until at least 1 byte arrives
            if not data:                 # empty = peer sent FIN
                print("[server] client hung up")
                return
            print(f"[server] recv {len(data):4d} bytes: {data!r}")
            conn.sendall(b"echo: " + data)
 
 
# ---------- naive client ----------
def client(msg: str) -> None:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((HOST, PORT))
        # Two sends back-to-back — watch what the server sees.
        s.sendall(msg.encode())
        s.sendall(b" [second-write]")
        reply = s.recv(4096)
        print(f"[client] got {reply!r}")
 
 
# ---------- framed client (the fix) ----------
def framed_client(msg: str) -> None:
    """Length-prefix each message so the receiver knows where it ends."""
    payload = msg.encode()
    header = struct.pack("!I", len(payload))   # 4-byte big-endian length
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((HOST, PORT))
        s.sendall(header + payload)
        # Read the length header first, then exactly that many bytes.
        raw_len = recv_exact(s, 4)
        (msg_len,) = struct.unpack("!I", raw_len)
        body = recv_exact(s, msg_len - len(b"echo: ") + len(b"echo: "))
        print(f"[framed-client] got {body!r}")
 
 
def recv_exact(s: socket.socket, n: int) -> bytes:
    """Read exactly n bytes or die trying. This is the pattern real protocols use."""
    buf = bytearray()
    while len(buf) < n:
        chunk = s.recv(n - len(buf))
        if not chunk:
            raise ConnectionError(f"peer closed after {len(buf)}/{n} bytes")
        buf.extend(chunk)
    return bytes(buf)
 
 
# ---------- entrypoint ----------
if __name__ == "__main__":
    mode = sys.argv[1] if len(sys.argv) > 1 else "server"
    if mode == "server":
        server()
    elif mode == "client":
        client(" ".join(sys.argv[2:]) or "hello")
    elif mode == "framed-client":
        framed_client(" ".join(sys.argv[2:]) or "hello")
    else:
        sys.exit(f"unknown mode: {mode}")

Anatomy of the script — what to notice, line by line

The lines worth reading twice

Line 22 · SO_REUSEADDR
Without this, restarting the server within ~60s fails with ‘Address already in use’ because the previous socket is in TIME_WAIT. Every real server sets this.
socket-opt
Line 24 · listen(5)
5 is the backlog — how many pending connections the kernel queues before your accept() catches up. On a busy server this is often 128 or 1024.
backlog
Line 30 · threading.Thread(daemon=True)
One thread per connection. Fine for a demo; production uses asyncio or epoll to handle 10k+ connections in one thread (see S066 · Concurrency Models).
concurrency
Line 37 · conn.recv(1024)
‘Give me up to 1024 bytes.’ Might return 1 byte, might return 1024. Never blocks past the first arriving byte. This is where the byte-stream surprise lives.
byte-stream
Line 39 · if not data: return
Empty bytes = peer sent FIN (graceful close). The socket is still open on our side until we close it too. This detection is how you avoid busy-looping.
half-close
Line 62 · struct.pack('!I', len(payload))
The length-prefix framing trick. ‘!’ = network byte order (big-endian); ‘I’ = unsigned 32-bit int. Now the receiver can call recv_exact(4) and know exactly how much to read next.
framing
Line 71 · recv_exact()
The pattern every real protocol library implements. TCP may split your 100-byte message into two recv() calls of 60 + 40 bytes. You must loop until you have what you asked for.
correctness

Run it and observe

# terminal 1
python3 echo.py server
 
# terminal 2 — naive client (two back-to-back sends)
python3 echo.py client "hello world"
 
# what the server prints will often look like:
# [server] recv   25 bytes: b'hello world [second-write]'
# NOT two separate recvs — that's the byte stream merging your two sends.

Now inspect the socket state from a third terminal while a connection is alive:

# Linux: list all TCP sockets with process info
ss -tnp | grep 9999
 
# You'll see two rows:
# ESTAB  0  0  127.0.0.1:9999    127.0.0.1:54321   users:(("python3",pid=1234,fd=4))
# LISTEN 0  5  127.0.0.1:9999    0.0.0.0:*         users:(("python3",pid=1234,fd=3))
 
# And see the actual packets on the loopback interface:
sudo tcpdump -i lo -X port 9999
# You'll literally see SYN, SYN-ACK, ACK, PSH, FIN in the output.
Try itFeel the byte-stream problem, then fix it with framing.
  1. Start the server, then run the naive client three times in a row. Watch how the server sometimes prints one big recv and sometimes two smaller ones — TCP is deciding for you.
  2. Now run python3 echo.py framed-client "hello world". The framing header (\x00\x00\x00\x0b = length 11) is visible in the server's recv output.
  3. Stretch: modify the server to read 4-byte length headers with recv_exact(4), then recv_exact(length). You've just implemented the same wire format Kafka uses.
💡 Hint · Compare the raw server output between naive client and framed-client. In the naive case the two writes concatenate on the receiver; in the framed case each length prefix tells the server exactly where one message ends and the next begins. This is why gRPC, Kafka, WebSockets, and every real protocol prefix messages with a length header.

(d) Production reality · 15 min

Every senior backend engineer has been paged for one of these three failure modes. All three are real, all three took large chunks of the internet down, and all three are worth studying in detail because the same failure modes still happen today at smaller scale.

War story Cloudflare (via Verizon / DQE Communications)· 2019~15 % of Cloudflare traffic, 2 hours
🔥 What broke

On 24 June 2019, a small Pennsylvania ISP called DQE Communications ran a "BGP optimiser" that reannounced several thousand of Cloudflare's, Amazon's, and other providers' IP prefixes as if DQE were the origin — but more specific (longer prefix) than the real routes.

DQE's upstream, Allegheny Technologies, passed the leak to Verizon, who accepted the routes and re-advertised them to the rest of the global BGP table. Because BGP prefers more-specific prefixes, huge chunks of Cloudflare-bound traffic got routed to a tiny steel-mill ISP in Pennsylvania. Websites went dark globally for ~2 hours.

🧯 The fix

Cloudflare called Verizon and NOCs around the world to withdraw the routes manually. The permanent fix is the industry-wide push for RPKI (Resource Public Key Infrastructure) — cryptographically signed route origin authorisations so upstreams can validate what they're being asked to route.

Cloudflare's Nick Coghlan wrote the definitive blog post the same day. Read it — it's the clearest incident writeup you'll ever see.

🎓 Lesson to steal
The internet's routing plane runs on trust. Any ISP can, technically, announce any prefix — and until every network deploys RPKI, we're one misconfigured optimiser away from repeating this. Your service's uptime depends on strangers' router configs.
Post-mortem
War story Facebook / Meta· 2021~6 hours, 3.5 B users, revenue est. \$60M+
🔥 What broke

On 4 October 2021, Facebook engineers ran a routine backbone capacity-audit command that — due to a bug in the audit tool — withdrew every BGP route to Facebook's authoritative DNS servers.

From the internet's point of view, Facebook's DNS ceased to exist. That meant facebook.com, instagram.com, whatsapp.com, and every internal service that resolved through Facebook DNS all became unreachable — including the tools engineers needed to fix the problem. Employee badges stopped working because they auth'd through the same broken DNS.

🧯 The fix

Engineers had to physically enter data centres and manually reset routers. Cloudflare (whose 1.1.1.1 resolvers were hammered with retries) wrote a beautiful post-mortem showing the exact moment Facebook's BGP announcements disappeared from the global table.

Meta's own postmortem admitted: their audit tool's guardrail against exactly this failure had a bug, and their DNS servers were configured to withdraw themselves if they couldn't reach the backbone — a "safety" that turned into a suicide pact.

🎓 Lesson to steal
DNS is a single point of failure even when it's ‘distributed.’ If your auth DNS goes dark, nothing else matters — LB, CDN, database, employee badges. Split your DNS across two independent providers (many teams use Route 53 + NS1 or similar).
Post-mortem
War story AWS us-east-1· 2021~7 hours, Netflix / Disney+ / Ring / Robinhood down
🔥 What broke

On 7 December 2021, an automated capacity-scaling event in AWS's internal network triggered unexpected behaviour in a large number of clients on the internal AWS backbone. Those clients started connection-storming the internal DNS service, which then couldn't scale up fast enough.

Because AWS's own control plane (EC2, Lambda, SQS APIs) uses that same internal DNS, control-plane APIs started timing out. Customers couldn't launch new instances, autoscalers failed, and the many services that treat "us-east-1 is always up" as a design assumption cascaded down.

🧯 The fix

AWS engineers had to manually throttle the internal traffic pattern and add capacity. The postmortem highlighted that the automation itself lacked good throttling: when the "clients" retried at once, they made the recovery harder.

AWS later hardened internal DNS with better rate limits and pushed harder on multi-region design guidance for customers.

🎓 Lesson to steal
Even inside the world's most redundant cloud, DNS + retry storms + a self-referential control plane can take everything down. The takeaway for your app: add exponential backoff + jitter to every retry, and never treat a single region as HA.
Post-mortem

The everyday gotchas (not headline-grabbing, but they'll page you)

Where this shows up next in the plan

Networking I feeds every backend session that follows
S062 · Load Balancers L4 vs L7
Everything you learned about sockets and TCP handshakes now happens twice — once client→LB, once LB→backend.
S063 · HTTP/HTTPS + TLS handshake
The TLS layer we hand-waved in the mermaid diagram — cipher suites, ALPN, session resumption, cert chains.
S064 · Caching + CDNs
Anycast + BGP + geographic routing — the good side of the same tech that broke in the war stories.
S066 · Concurrency Models
Why ‘one thread per socket’ dies at 10k connections and how epoll/kqueue/io_uring saves you.
S078 · Observability I
How to instrument sockets so ‘the network is slow’ becomes ‘p99 syn-to-ack latency to eu-west-2 is 340 ms starting 03:14 UTC.’
S089 · SRE — Incident response
The playbook you'd wish Facebook had when their audit tool nuked their own DNS.

(e) Recall + stretch · 10 min

Recall — click each to reveal · click to reveal
★ = stretch question

Explain-out-loud test

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

  1. What happens between typing curl example.com and getting HTML back? (in one minute, naming DNS, TCP handshake, HTTP)
  2. Why is TCP a byte stream, and what's one concrete bug that creates? (with a fix)
  3. Name one real production outage where DNS or BGP took down a household-name company — and the one-line lesson.

Hub: The 6-Month Learning Plan


Part of a 130-session evergreen learning series. Session structure: intuition → visual → hands-on → production war stories → recall. Duration: 60 minutes.