AI EngineeringZero to ProductionHome·About·What’s new·Contact
System Design · Chapter SD10

Computer networks

Every distributed system in this track talks over a network, and the network has its own laws. This lesson builds the layered model, TCP vs UDP, the handshake, IP/DNS, and HTTPS/TLS — then walks the full path of typing a URL. Runnable models of a checksum and a sliding-window sender make the reliability machinery concrete.

⏱️ ~3.5 hours🧪 5 labs🎯 Beginner→Expert
🌱 Start here — from zeroComputer networks, from scratch. A network moves bytes between machines that may be on the other side of the planet, over links that lose, reorder, and delay packets. The genius of the design is layering: each layer solves one problem and hands a clean abstraction up. This chapter walks the layers, contrasts TCP and UDP, and traces exactly what happens when you type a URL. The reliability sims (checksum, sliding window) are models of the algorithms, not a real network stack.

Learning objectives

  • Map the OSI and TCP/IP layer models and say what each layer does.
  • Choose TCP vs UDP for a workload and explain the 3-way handshake.
  • Explain IP addressing, routing, and how DNS resolves a name.
  • Describe the HTTPS/TLS handshake and what it guarantees.
  • Trace end-to-end what happens when you type a URL and press enter.
▶ Runnable companionThe checksum and sliding-window labs are plain-Python models of the reliability mechanisms TCP uses — they reproduce the logic (detect corruption, keep a window of unacked packets) without touching a real socket.

1 · Layering — OSI vs TCP/IP

Start with a 3am story every on-call engineer eventually lives. A page fires: "checkout is down." You SSH in. Is it the app? The database? The load balancer? The DNS? The customer's flaky Wi-Fi? A cloud provider outage two hops away? Without a way to divide the problem, you're staring at an infinite haystack. The single most valuable thing networking gives you is not a protocol — it's a way to slice that haystack into layers, so a failure belongs to exactly one of them and you know whose problem it is.

Here's the everyday analogy that makes it click: the postal system. When you mail a birthday card, you write the message (that's your job — the content), seal it in an envelope with an address (the postal service's job — delivery), and the postal service in turn hands it to trucks and planes (the transport company's job — physical movement). You do not think about jet fuel when writing "Happy Birthday." Each party trusts the layer below to do its one job and hand up a clean promise: "give me an addressed envelope and I'll get it there." Networks are built exactly this way — a stack of layers, each depending only on the clean abstraction the one below hands it.

Why layering is worth the ceremony: it lets you swap any single layer without touching the others. Your HTTP code doesn't change when your laptop moves from Ethernet to Wi-Fi, because the link layer changed underneath while its promise — "move this frame one hop" — stayed identical. That's the same win as the abstract-data-type idea from the DSA track: depend on the contract, not the machinery. The common mistake beginners make is skipping layers in their mental model — assuming "the network is slow" when the real culprit is a DNS lookup (application layer) or a saturated link (link layer). When you can name the layer, you can name the fix.

Now the precise version. Each layer uses the layer below and serves the layer above, so you can reason about (say) HTTP without thinking about voltage on a wire. The OSI model has seven layers (a teaching reference); the TCP/IP model that the internet actually runs has four. Data moving down the stack is encapsulated — each layer wraps the layer above in its own header.

TCP/IP layerJobExamples≈ OSI layers
Applicationapp-level messagesHTTP, DNS, TLS5–7
Transportend-to-end deliveryTCP, UDP4
Internetaddressing & routingIP, ICMP3
Linkone physical hopEthernet, Wi-Fi1–2
Application HTTP/DNS/TLS Transport TCP/UDP Internet IP Link Ethernet/Wi-Fi
🗺️ How to read this diagram

Read it top-to-bottom: your data starts at the top layer and each downward arrow hands it to the layer below, closer to the wire.

  • The top box, Application, is where your program lives — the side label lists the protocols that speak here: HTTP, DNS, TLS.
  • Each downward arrow passes the data to the next layer down: Transport (TCP/UDP), then Internet (IP), then Link (Ethernet/Wi-Fi) — the four boxes are colour-tiered so each layer is a distinct band.
  • The payoff is the ordering: by the time you reach the bottom Link box the data is ready for the physical medium, and the far side climbs the same stack back up — one layer added on the way down, the same layer stripped on the way up.

In short: Four stacked layers, each solving one problem and handing off to the next — Application on top, Link on the wire at the bottom.

Encapsulation in one sentenceYour HTTP request is put inside a TCP segment, which is put inside an IP packet, which is put inside a link-layer frame — like nested envelopes, each added by one layer and stripped by the same layer on the far side.

2 · TCP vs UDP & the 3-way handshake

Picture two ways to have a conversation. The first is a phone call: you say "hello?", the other person says "hello, I hear you," you say "great, I hear you too," and only then do you start talking — and if a sentence gets garbled, you say "sorry, what?" and they repeat it. The second is shouting a score across a noisy stadium: you yell the number, and if your friend didn't catch it, oh well — you'll yell the next one in a second anyway. That's TCP versus UDP in one image. TCP is the phone call: it sets up a connection, confirms every word landed, and re-sends anything lost. UDP is the stadium shout: no setup, no confirmation, no re-sends — just fire and move on.

Why would anyone ever choose the "unreliable" one? This is the part that trips people up. It feels obvious that reliable-and-ordered is always better — so why does DNS, video calling, and gaming reach for UDP? Because reliability has a cost, and for some data that cost is worse than the loss. Think about your friend's position in a live game: if a packet saying "player is at (10, 20)" gets lost, you do not want TCP to stall everything and re-send it — by the time it arrives the player has moved to (14, 25) and the old position is a lie. You'd rather drop the stale packet and use the next fresh one. TCP's guarantee — "I will deliver every byte, in order, even if I have to wait" — becomes actively harmful when late data is worthless. That waiting-for-the-missing-piece stall has a name you'll hear in interviews: head-of-line blocking.

The common mistake is treating "use TCP" as the safe default for everything. It's the safe default for most things — web pages, APIs, file transfers, anything where a missing byte corrupts the meaning — but the moment your data is a stream of perishable snapshots (positions, audio samples, sensor readings), UDP's willingness to drop-and-continue is the feature, not the bug. The question to ask isn't "which is more reliable?" but "for my data, is a late-but-complete delivery better or worse than an on-time-but-lossy one?"

Now the precise definitions. TCP gives a reliable, ordered byte stream: it retransmits lost data, reorders what arrives out of order, and controls its sending rate. UDP gives none of that — it just fires datagrams and hopes. You pay for TCP's guarantees with latency (handshakes, acknowledgements); UDP trades reliability for speed, which is why live video, games, and DNS often use it. A TCP connection opens with a 3-way handshake: SYN → SYN-ACK → ACK, which synchronises sequence numbers on both sides before any data flows.

SYN SYN-ACK ACK data
🗺️ How to read this diagram

Read it left to right: each arrow is one message crossing the wire, and the sequence must complete before the last box begins.

  • The first box, SYN, is the client's opening request to synchronise; the arrow carries it to the second box.
  • The middle two boxes, SYN-ACK then ACK, are the reply and its confirmation — three coloured messages in strict order that make up the "3-way" handshake.
  • The payoff is the final box, data: it sits after all three handshake arrows, showing that no actual data flows until the connection is fully established.

In short: SYN → SYN-ACK → ACK happens first; only then does data move — the setup cost you pay for a reliable connection.

TCPUDP
Deliveryreliable, orderedbest-effort
Connectionyes (handshake)no
Overheadhigher (acks, retransmit)minimal
Use forweb, APIs, file transfervideo, games, DNS, VoIP
Example code for learning — review, test, and adapt it before running against real or production systems. Commands can create, change, or delete resources. See the Terms & Disclaimer.
Step 1 · The handshake as a state machine (model)
handshake.py# MODEL: the client/server sequence-number exchange of a TCP open.
def three_way_handshake(client_isn, server_isn):
    log = []
    # 1. client -> server: SYN, seq = client_isn
    log.append(("C->S", "SYN", client_isn))
    # 2. server -> client: SYN-ACK, seq = server_isn, ack = client_isn + 1
    log.append(("S->C", "SYN-ACK", server_isn, client_isn + 1))
    # 3. client -> server: ACK, ack = server_isn + 1
    log.append(("C->S", "ACK", server_isn + 1))
    established = True
    return established, log

ok, log = three_way_handshake(client_isn=1000, server_isn=5000)
for step in log:
    print(step)
print("connection established:", ok)
('C->S', 'SYN', 1000)
('S->C', 'SYN-ACK', 5000, 1001)
('C->S', 'ACK', 5001)
connection established: True

3 · IP addressing, routing & DNS

Try to remember the last phone number you dialled from memory. You probably can't — you tapped a name in your contacts and the phone looked up the digits. The internet has the exact same split, and it's easy to forget it's even happening. When you type studybydoing.in, no computer anywhere knows what that means — machines route to numbers, not names. Something has to translate the human-friendly name into the machine-friendly address before a single byte can move. That translation is DNS, and it runs invisibly before every connection you make, thousands of times a day.

Here's the mental model. An IP address is like a street address for a building: it says where on the network a machine sits. Routing is the postal sorting network that gets an envelope from your local post office to that address — no single router knows the whole path, it just knows "for anything headed that direction, hand it to this next office," and enough of those hops chain together to reach the destination. And DNS is the phone book that turns the name you remember into the number the routers need. The reason this is three separate things — address, routing, naming — is the layering lesson again: each solves one problem, and you can change your building's tenant (the name→IP mapping) without re-drawing every road (the routes).

The mistake worth flagging: people imagine DNS as "one big lookup table in the sky." It isn't — there is no single machine that knows every name on earth, and there couldn't be (it would be a bottleneck and a single point of failure for the whole internet). Instead the answer is found by walking a hierarchy: a root server points you to the server that handles .in, which points you to the server that handles studybydoing.in, which finally hands back the IP. That sounds slow — several round-trips just to learn one number — which is exactly why caching lives at every level, and why the very next callout says caching is what makes the web feel fast.

Now precisely. An IP address identifies an interface on the network; a packet carries a source and destination IP. Routing is how packets hop from router to router toward the destination network — each router consults a table and forwards to the best next hop. But humans use names, not numbers, so DNS (the Domain Name System) resolves a name like example.com to an IP, walking from the root servers down to the authoritative server, with caching at every level to keep it fast.

example.com Resolver Root/TLD/Auth 93.184.x.x
🗺️ How to read this diagram

Follow the arrows left to right: this is one name being turned into a number, box by box.

  • The first box, example.com, is the human-friendly name you typed — a machine can't route to it directly.
  • The middle boxes, Resolver then Root/TLD/Auth, are the lookup chain: the resolver walks the hierarchy of servers, asking each level in turn for the next.
  • The payoff is the last green box, 93.184.x.x — the actual IP address handed back, which is what the routers need to move packets.

In short: DNS turns the name on the left into the IP on the right by walking a chain of servers — name in, number out.

Step 2 · A recursive DNS resolver with caching (model)
dns.py# MODEL: a resolver that walks root -> TLD -> authoritative, caching answers.
ROOT = {"com": "tld-com-server"}
TLD  = {"example.com": "ns.example.com"}
AUTH = {"example.com": "93.184.216.34"}

class Resolver:
    def __init__(self):
        self.cache = {}
    def resolve(self, name):
        if name in self.cache:
            return ("cache", self.cache[name])
        tld = name.split(".")[-1]
        ROOT[tld]                       # ask root for the TLD server
        TLD[name]                       # ask TLD for the authoritative server
        ip = AUTH[name]                 # ask authoritative for the A record
        self.cache[name] = ip
        return ("resolved", ip)

r = Resolver()
print(r.resolve("example.com"))         # walks the chain
print(r.resolve("example.com"))         # served from cache
('resolved', '93.184.216.34')
('cache', '93.184.216.34')
DNS caching is why the web is fastThe first lookup walks the hierarchy; every later one hits a cache (in your OS, your resolver, or the browser) until the record's TTL expires. This is the SD5 cache pattern again — recompute rarely, serve from memory usually.

4 · HTTP, HTTPS & the TLS handshake

Imagine mailing a postcard versus a sealed, signed, tamper-evident courier envelope. Plain HTTP is the postcard: anyone who handles it along the way — the coffee-shop Wi-Fi, your ISP, a router in some data centre — can read every word, and worse, could rewrite it before passing it on, and neither you nor the recipient would ever know. For years that's how the web worked, and it's why typing your credit card into an http:// page was genuinely dangerous. HTTPS is the courier envelope: sealed so no one can read it, tamper-evident so any meddling is detected, and signed so you're sure it really came from who it claims. That upgrade is TLS, and understanding what it does — and doesn't — promise is one of the highest-leverage things on this whole page.

Let's separate the two ideas that people constantly blur together. HTTP is the language — the grammar of "here's a request (a verb like GET, a path, some headers, maybe a body), here's a response (a status code and body)." TLS is the sealed tunnel the language travels through. HTTPS is simply "HTTP spoken inside a TLS tunnel." Crucially, TLS knows nothing about web pages — it would just as happily wrap email or any other protocol. Keeping "the message" (HTTP) separate from "the secure pipe" (TLS) is, once again, the layering payoff: you can harden the pipe without changing a word of the language.

The three guarantees, and the mistake of conflating them. Newcomers say "TLS encrypts my traffic" and stop there — but encryption is only one of three distinct promises, and the other two matter just as much. Confidentiality (encryption) stops someone reading your data. But an attacker who can't read your POST body might still try to change it in flight — that's stopped by integrity, a tamper-detection tag (a MAC) that makes any edit fail verification. And even a perfectly encrypted, tamper-proof channel is worthless if you're unknowingly talking to an impostor — that's stopped by authentication, where the server's certificate proves it's really who it claims. Confidentiality without authentication is like whispering a secret to a stranger who said they were your bank. All three, together, are what the padlock icon actually means.

Now the precise version. HTTP is the application-layer request/response protocol of the web: a method (GET, POST), a path, headers, and an optional body; the server replies with a status code and body. HTTPS is HTTP running inside a TLS tunnel. The TLS handshake (after the TCP handshake) authenticates the server via its certificate and negotiates a shared symmetric key, so everything afterward is encrypted and tamper-evident. TLS gives three guarantees: confidentiality (eavesdroppers see ciphertext), integrity (tampering is detected), and authentication (you're really talking to that server).

TCP open ClientHello Cert +key exchange Encrypted HTTP
🗺️ How to read this diagram

Read it left to right: each box is a step that must finish before the next, building from a bare connection up to a secure one.

  • The first box, TCP open, is the plain connection from §2 — TLS only starts after TCP is already established.
  • The middle boxes, ClientHello then Cert + key exchange, are the handshake itself: the client proposes, the server proves its identity with a certificate, and both agree a shared symmetric key.
  • The payoff is the last green box, Encrypted HTTP — once the key is settled, every HTTP byte afterward travels sealed and tamper-evident inside the tunnel.

In short: TCP first, then a TLS handshake to authenticate and agree a key, and only then does encrypted HTTP flow.

Step 3 · Modelling the TLS guarantees
tls_model.py# MODEL: a key exchange producing a shared secret, then an integrity check.
# This illustrates WHAT TLS guarantees, not the real cryptographic protocol.
import hashlib, hmac

def derive_shared_key(client_random, server_random, premaster):
    seed = f"{client_random}:{server_random}:{premaster}".encode()
    return hashlib.sha256(seed).hexdigest()   # stand-in for the TLS PRF

def mac(key, message):                        # integrity tag
    return hmac.new(key.encode(), message.encode(), hashlib.sha256).hexdigest()

key = derive_shared_key("cr-123", "sr-456", "premaster-secret")
msg = "GET /account HTTP/1.1"
tag = mac(key, msg)
print("shared key (first 16):", key[:16])
# receiver recomputes the tag; a tampered message would not match
print("integrity ok:", hmac.compare_digest(tag, mac(key, msg)))
print("tamper detected:", not hmac.compare_digest(tag, mac(key, "GET /admin HTTP/1.1")))
shared key (first 16): fb61405c41b1c1df
integrity ok: True
tamper detected: True
This is a model, not real TLSReal TLS uses asymmetric crypto (certificates, ephemeral Diffie–Hellman) to agree the key and negotiate cipher suites and versions. The lab shows the outcome — a shared symmetric key plus a MAC for integrity — so you can reason about the guarantees without reimplementing the protocol.

5 · Reliability — checksums, windows, congestion & flow control

Here's the fact that should quietly amaze you. The layer TCP is built on — IP — makes no promises whatsoever. IP can lose your packet, deliver it twice, deliver packets out of order, or hand you a packet with a bit flipped by cosmic-ray-level noise on some undersea cable. It's a postal service that shrugs and says "I'll probably get most of it there, in roughly some order, mostly intact — no guarantees." And yet when you download a file it arrives byte-perfect and in order every single time. That reliability is not in the network; it's a set of clever algorithms TCP runs at the two endpoints, papering over an unreliable middle. This section is how that magic actually works.

Think of it as four separate problems, each with its own trick. How do I know a packet arrived undamaged? A checksum — a little fingerprint of the bytes that the receiver recomputes; if it doesn't match, the packet is corrupt and gets thrown away. How do I know a packet arrived at all? Sequence numbers and acknowledgements — every packet is numbered, the receiver says "got up to number 7," and anything unacknowledged gets re-sent. How do I go fast without a re-send stall after every packet? A sliding window — instead of ship-one-wait-for-ack (agonizingly slow on a long link), the sender keeps several packets in flight at once. And how fast is safe? Two feedback loops, described below.

The two "how fast" loops are the ones people mix up, so pin the difference now. Flow control protects the receiver: a phone with a tiny buffer can tell a beefy server "slow down, I can only take this much," and the server obeys the receiver's advertised window. Congestion control protects the network in between: even if the receiver is fast, the twenty routers on the path might be jammed, so TCP starts cautiously (slow start), speeds up while things are going well, and backs off the instant it sees loss — treating a dropped packet as the network whispering "you're pushing too hard." The mistake is thinking one knob does both; they answer different questions ("is the far end overwhelmed?" vs "is the road overwhelmed?") and can each throttle you independently.

Now precisely. TCP turns an unreliable IP layer into a reliable stream with a few mechanisms. A checksum detects corrupted packets. Sequence numbers + acknowledgements detect loss and reordering. A sliding window lets the sender have several packets in flight at once (not stop-and-wait) while bounding how far ahead it may run. Two feedback loops shape the rate: flow control stops a fast sender from overwhelming a slow receiver (the receiver advertises its window), and congestion control stops senders from overwhelming the network (slow start, then back off on loss).

stop-and-wait (window = 1) send 0 ack 0 1 packet per RTT — pipe mostly idle sliding window (window = 3) send 0 send 1 send 2 acks → window slides 3 packets per RTT — pipe kept full more in flight per round-trip = higher throughput on a long link The window is the throughput knob. Stop-and-wait pays a full round-trip for every single packet; a window of 3 keeps three packets in flight per round-trip, so it drains the same backlog in a third of the time.
🗺️ How to read this diagram

Compare the two rows top-to-bottom: the top is the slow stop-and-wait scheme, the bottom is the sliding window, both sending over the same link.

  • The top red box sends one packet, then waits for its ack (the blue box) before sending the next — one packet per round-trip, so the link sits idle most of the time.
  • The bottom green boxes are three packets fired back-to-back before any ack returns — that is the window. As acks come back, the amber window slides forward to admit the next packets.
  • The payoff is the two coloured captions: same link, but the window pushes three packets per round-trip instead of one — the pipe stays full instead of idle.

In short: Stop-and-wait wastes a round-trip per packet; a window of W sends W packets per round-trip, and the ideal W is the bandwidth-delay product — just enough to keep the pipe full.

Step 4 · A checksum + sliding-window sender (model)
reliability.py# MODEL: 16-bit ones-complement-style checksum + a sliding window sender.
def checksum(data: bytes) -> int:
    total = 0
    for b in data:
        total = (total + b) & 0xFFFF        # keep it 16-bit
    return (~total) & 0xFFFF                # ones-complement

def corrupted(data, sent_ck):
    return checksum(data) != sent_ck        # receiver recomputes and compares

msg = b"hello-network"
ck = checksum(msg)
print("checksum ok:", not corrupted(msg, ck))
print("corruption caught:", corrupted(b"hallo-network", ck))

# Sliding window: at most `window` unacked packets in flight at once.
def send_with_window(packets, window):
    base, next_seq, acked = 0, 0, []
    timeline = []
    while base < len(packets):
        # send everything the window allows
        while next_seq < len(packets) and next_seq < base + window:
            timeline.append(("send", next_seq)); next_seq += 1
        # receiver acks the oldest in order; window slides forward
        timeline.append(("ack", base)); acked.append(base); base += 1
    return timeline, acked

tl, acked = send_with_window(list(range(6)), window=3)
print("in-flight cap = 3; acked in order:", acked)
print("first 5 events:", tl[:5])
checksum ok: True
corruption caught: True
in-flight cap = 3; acked in order: [0, 1, 2, 3, 4, 5]
first 5 events: [('send', 0), ('send', 1), ('send', 2), ('ack', 0), ('send', 3)]
Window size is the throughput knobStop-and-wait (window = 1) wastes the link waiting for each ack; a larger window keeps more data in flight and fills the pipe. The right window is roughly the bandwidth × round-trip-time product — a direct tie to the latency thinking in SD5/SD6.

6 · What happens when you type a URL

If you interview for any backend, infra, or full-stack role, you will be asked this question. "What happens when you type a URL and hit enter?" It's a rite of passage — not because interviewers care about the trivia, but because it's the one question that reveals whether the layers in your head are connected or just a pile of memorized facts. A weak answer names some acronyms. A strong answer walks a single request down through every layer and back up, and can stop at any step to say "and if this were slow, here's how I'd tell, and here's the fix." That's the difference between having read about networks and actually understanding them.

The reason this makes such a good capstone is that nothing here is new — every box on the path is a section you just built. The name-to-number lookup is DNS (§3). The connection setup is the 3-way handshake (§2). The sealed tunnel is TLS (§4). The request and response are HTTP (§4), wrapped in the encapsulated envelopes of §1, carried reliably by the windows and acknowledgements of §5. The walk below is really a guided tour proving those pieces click together into one continuous journey.

One framing that turns this from recitation into insight: read the list as a latency budget. Each step costs time — a DNS miss can be tens of milliseconds, each handshake round-trip is one full round-trip-time, the server might think for a while, the bytes take time to transfer. When a page feels slow, the skill is attributing the delay to the right step, because each has a different fix (cache the DNS, reuse the connection, speed up the app tier, put a CDN closer). The common mistake is to treat "the site is slow" as one undifferentiated problem; the layered walk is exactly the tool that lets you say which step is the culprit — which is what Exercise 6 has you do.

type URL & press enter → one request walks every layer, left to right DNS §3 · name→IP TCP §2 · handshake TLS §4 · encrypt HTTP §4 · GET / Server SD5 · LB+cache Render paint page each box costs time — a slow page = one dominant phase, and each phase has its own fix nothing new here: every box is a section you already built The capstone is just the earlier sections in a row. One request threads DNS → TCP → TLS → HTTP → server → render; read it as a latency budget and a slow page becomes one dominant phase with a known fix.
🗺️ How to read this diagram

Follow the arrows left to right: this is the single journey of one request from the moment you press enter to the painted page.

  • Each box is one phase, and its small label names the earlier section that built it — DNS (§3), the TCP handshake (§2), the TLS tunnel (§4), the HTTP request (§4), then the server (load balancer + cache, SD5).
  • The arrows are the ordering: nothing can start until the box before it finishes, which is why they run strictly in sequence rather than all at once.
  • The payoff is reading the row as a latency budget — each box costs time, so a slow page is almost always one dominant box, and naming that box (not "the site is slow") is what points you at the right fix.

In short: The URL walk is every section on this page chained into one path; treat it as a per-phase time budget and you can attribute — and fix — slowness at the right layer.

From https://studybydoing.in to a rendered page:

  1. DNS resolution — the browser resolves example.com to an IP (§3), hitting caches first.
  2. TCP handshake — a 3-way SYN/SYN-ACK/ACK opens a connection to that IP on port 443 (§2).
  3. TLS handshake — client and server authenticate and agree a symmetric key; the channel is now encrypted (§4).
  4. HTTP request — the browser sends GET / with headers, inside the TLS tunnel, carried by TCP over IP (§1 encapsulation).
  5. Server processing — a load balancer picks an app server (SD5), which may hit a cache or database and returns an HTTP response.
  6. Render — the browser parses HTML, and fetches CSS/JS/images (often reusing the same connection), then paints the page.
Every box you built shows up hereDNS caching, the handshake, TLS, load balancing, and app caching are all on this single path. When an interviewer asks "what happens when you type a URL," they are checking whether you can connect all of these layers end to end.

✓ Checkpoint — you can move on when you can…

  • Map the four TCP/IP layers and give an example protocol at each.
  • Choose TCP vs UDP for a workload and justify it.
  • Explain the 3-way handshake and why sequence numbers matter.
  • Say what TLS guarantees and where its handshake sits relative to TCP.
  • Walk the full URL-to-page path without skipping a layer.

Knowledge check

check yourself
✓ Knowledge check

A live multiplayer game sends 60 position updates per second. Would you build it on TCP or UDP, and why does the "wrong" choice actually hurt here?

Show answer
Use UDP. A position update that arrives late is worthless — the game has already moved on — so TCP's guarantee of in-order, retransmitted delivery is exactly the wrong thing: a single lost packet would stall the stream (head-of-line blocking) while TCP retransmits stale data. UDP lets the game drop the old update and use the next fresh one, trading reliability for the low latency the workload actually needs.
✓ Knowledge check

HTTPS runs HTTP inside TLS. Name the three guarantees TLS provides, and state which one stops an attacker who can read and modify traffic from silently changing your POST body.

Show answer
TLS provides confidentiality (encryption), integrity (tamper detection via a MAC), and authentication (the certificate proves the server's identity). Integrity is what stops silent modification: any change to the ciphertext makes the MAC fail to verify, so the tampered message is rejected rather than acted on.

🪜 Practice ladder beginner → industry

Six graded exercises, easy to real-world. Try each before opening its solution.

Exercise 1 · Classify protocols into layersBeginner

Context: Knowing which layer a protocol lives in is the first step to reasoning about a network problem — it tells you whose job a given failure is.

Your task: Given a list of protocols (HTTP, TCP, IP, Ethernet, DNS, UDP, TLS), map each to its TCP/IP layer and print the grouping.

Requirements:

  • Use the four TCP/IP layers: Application, Transport, Internet, Link
  • Place TCP and UDP at Transport, IP at Internet, Ethernet at Link
  • Place HTTP, DNS and TLS at Application
  • Print each layer with the protocols assigned to it

💡 Hint: A dict from protocol to layer, then invert it to group by layer.

Show solution

Map each protocol to its layer, then group:

layer_of = {
    "HTTP": "Application", "DNS": "Application", "TLS": "Application",
    "TCP": "Transport", "UDP": "Transport",
    "IP": "Internet",
    "Ethernet": "Link",
}
from collections import defaultdict
grouped = defaultdict(list)
for proto, layer in layer_of.items():
    grouped[layer].append(proto)
for layer in ["Application", "Transport", "Internet", "Link"]:
    print(f"{layer:12s}: {grouped[layer]}")

TLS sits at the application layer (it runs over TCP); this is why a TLS problem is a different diagnosis from an IP-routing problem — layering localises the fault.

Exercise 2 · Model the 3-way handshakeIntermediate

Context: The handshake is where TCP establishes the shared sequence-number state that reliable delivery depends on; modelling it makes the SYN/ACK numbers concrete.

Your task: Model the client/server message exchange of a TCP open, tracking sequence and acknowledgement numbers, and confirm the connection ends ESTABLISHED.

Requirements:

  • Message 1: client SYN with its initial sequence number (ISN)
  • Message 2: server SYN-ACK with its ISN and ack = client ISN + 1
  • Message 3: client ACK with ack = server ISN + 1
  • Return the message log and an established flag

💡 Hint: Each ACK number is the next sequence byte the sender expects, i.e. the peer's ISN + 1.

Show solution

Three messages, each carrying the sequence/ack numbers:

def handshake(client_isn, server_isn):
    log = []
    log.append(("C->S", "SYN", {"seq": client_isn}))
    log.append(("S->C", "SYN-ACK", {"seq": server_isn, "ack": client_isn + 1}))
    log.append(("C->S", "ACK", {"ack": server_isn + 1}))
    return True, log

established, log = handshake(1000, 5000)
for m in log: print(m)
print("ESTABLISHED" if established else "FAILED")

The ack is always "the next byte I expect from you," i.e. your ISN + 1. After the third message both sides agree on each other's starting sequence number, so ordered, reliable delivery can begin.

Exercise 3 · A checksum that catches corruptionAdvanced

Context: Corruption detection is the most basic reliability guarantee; a checksum lets a receiver reject a damaged packet instead of acting on garbage.

Your task: Implement a 16-bit ones-complement-style checksum, and show it flags a single-bit/byte change in the payload.

Requirements:

  • Sum the bytes modulo 2^16, then take the ones-complement
  • A receiver recomputes the checksum and compares it to the sent value
  • Show an intact message verifies and a modified one fails
  • Note this catches many but not all corruptions (it is not cryptographic)

💡 Hint: Mask with & 0xFFFF to stay 16-bit; the final complement is ~total & 0xFFFF.

Show solution

Sum-and-complement, kept to 16 bits:

def checksum(data: bytes) -> int:
    total = 0
    for b in data:
        total = (total + b) & 0xFFFF
    return (~total) & 0xFFFF

def verify(data, sent_ck):
    return checksum(data) == sent_ck

msg = b"reliable-transport"
ck = checksum(msg)
print("intact verifies:", verify(msg, ck))               # True
print("one byte changed:", verify(b"reliqble-transport", ck))  # False

The intact message verifies; flipping one byte changes the sum and the check fails, so the receiver rejects it. A simple additive checksum catches many errors but not all (e.g. some transpositions) and provides no protection against a deliberate attacker — that is TLS's job, not the checksum's.

Exercise 4 · Sliding window vs stop-and-wait throughputExpert

Context: The sliding window is why TCP can fill a fast, high-latency link; comparing it to stop-and-wait quantifies the win.

Your task: Model the time to send N packets over a link with a given round-trip time under stop-and-wait (window 1) versus a window of W, and show the speedup.

Requirements:

  • Stop-and-wait sends one packet per RTT
  • A window of W sends up to W packets per RTT
  • Model total time as roughly ceil(N / window) × RTT
  • Report the time for window 1 vs window W and the speedup factor
  • Relate the ideal window to bandwidth × RTT

💡 Hint: The number of round-trips is ceil(N / window); throughput scales with the window until it hits the bandwidth-delay product.

Show solution

Model the number of round-trips each strategy needs:

import math

def send_time_ms(n_packets, window, rtt_ms=50):
    round_trips = math.ceil(n_packets / window)
    return round_trips * rtt_ms

N = 100
saw = send_time_ms(N, window=1)     # stop-and-wait
win = send_time_ms(N, window=10)
print(f"stop-and-wait: {saw} ms")   # 5000
print(f"window=10:     {win} ms")   # 500
print(f"speedup:       {saw / win:.0f}x")   # 10x

# ideal window ~ bandwidth-delay product
bandwidth_bps, rtt_s, pkt_bits = 10_000_000, 0.05, 12_000
bdp_packets = (bandwidth_bps * rtt_s) / pkt_bits
print(f"ideal window ~ {bdp_packets:.0f} packets")

Stop-and-wait pays one full RTT per packet; a window of 10 sends ten per RTT, a 10× speedup here. The window should be about the bandwidth-delay product — enough packets in flight to keep the pipe full — after which more window buys nothing and only risks congestion.

Exercise 5 · A recursive resolver with TTL cachingProfessional

Context: A resolver with TTL-aware caching is the real workhorse of DNS; expiring stale records correctly is what keeps names both fast and fresh.

Your task: Build a DNS resolver that walks a mock hierarchy on a miss and caches answers with a TTL, treating expired entries as misses so they are re-resolved.

Requirements:

  • Cache each answer with an expiry time (now + TTL)
  • On lookup, serve from cache only if the entry has not expired
  • On a miss or expiry, walk the mock root→TLD→authoritative chain
  • Show a cached hit, then an expired entry causing a fresh resolve
  • Label which path each lookup took

💡 Hint: Store (ip, expires_at) and compare a monotonic clock; a tiny sleep or a manual clock lets you demonstrate expiry deterministically.

Show solution

Cache the answer with an expiry and treat stale entries as misses:

import time

ROOT = {"com": "tld-com"}
TLD  = {"example.com": "ns.example.com"}
AUTH = {"example.com": "93.184.216.34"}

class Resolver:
    def __init__(self): self.cache = {}    # name -> (ip, expires_at)
    def resolve(self, name, ttl=1.0):
        now = time.monotonic()
        hit = self.cache.get(name)
        if hit and hit[1] > now:
            return ("cache", hit[0])
        _ = ROOT[name.split(".")[-1]]      # walk root
        _ = TLD[name]                      # walk TLD
        ip = AUTH[name]                    # authoritative answer
        self.cache[name] = (ip, now + ttl)
        return ("resolved", ip)

r = Resolver()
print(r.resolve("example.com", ttl=0.05))  # resolved
print(r.resolve("example.com", ttl=0.05))  # cache
time.sleep(0.06)
print(r.resolve("example.com", ttl=0.05))  # resolved again (expired)

Prints resolved, then cache, then resolved again after the TTL lapses. TTL-aware caching is the exact mechanism that makes DNS both fast (almost always a cache hit) and eventually consistent (records refresh when they expire).

Exercise 6 · Trace a URL load and attribute the latencyIndustry scenario

Context: Diagnosing a slow page load means attributing time to the right phase — DNS, connect, TLS, server, transfer — because each has a different fix.

Your task: Model the end-to-end latency of loading a URL as the sum of its phases (DNS, TCP, TLS, server, transfer), then identify the dominant phase and name the fix for it.

Requirements:

  • Represent each phase with a modelled millisecond cost
  • Sum them for the total page-load time
  • Identify the single largest phase
  • Map that phase to a concrete remedy (e.g. DNS caching, keep-alive, CDN, cache)
  • Label all numbers as a MODEL, not a measurement

💡 Hint: Reuse the connection (keep-alive / session resumption) to remove repeat DNS/TCP/TLS costs on the second request — that is often the biggest real-world win.

Show solution

Attribute the modelled latency to phases and read off the fix:

def load_url_model(phases_ms):
    """MODEL: phases_ms maps phase -> modelled milliseconds."""
    total = sum(phases_ms.values())
    worst = max(phases_ms, key=phases_ms.get)
    fixes = {
        "dns": "cache DNS / raise TTL",
        "tcp": "connection keep-alive / reuse",
        "tls": "session resumption / keep-alive",
        "server": "cache or speed up the app tier (SD5)",
        "transfer": "compression / CDN edge",
    }
    return total, worst, fixes[worst]

first_load = {"dns": 40, "tcp": 30, "tls": 50, "server": 120, "transfer": 60}
total, worst, fix = load_url_model(first_load)
print(f"total {total} ms; dominant phase: {worst} -> {fix}")

# second load reuses the connection: DNS/TCP/TLS drop out
second_load = {"dns": 0, "tcp": 0, "tls": 0, "server": 120, "transfer": 60}
print("second load total:", sum(second_load.values()), "ms")

On the first load the server phase dominates (120 ms), so the fix is app-tier caching (SD5). On the second load, keep-alive/session-resumption removes the DNS, TCP and TLS phases entirely — the single biggest real-world win for repeat requests. All figures here are modelled, not measured on your network.

© 2026 studybydoing.in · AI Engineering: Zero to Production · All rights reserved. · About · Privacy Policy · Terms · Contact
Educational content, provided as-is and without warranty. Code samples are examples — review, test, and adapt them before using in production. See the Terms of Use & Disclaimer. Use at your own risk.
© studybydoing.in