Frontend Labs
⋯

Part VII — The Transport Layer: TCP mechanics

TCP gives an application a reliable, ordered, bidirectional byte stream over unreliable IP. "Byte stream" means TCP does not preserve your message boundaries — 3 writes of 100 bytes may arrive as one read() of 300 or five reads of 60. Delimiting messages is the application's job (length prefixes, newlines, HTTP Content-Length, etc.).

23. The TCP header, field by field

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |       Destination Port        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Sequence Number                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Acknowledgment Number                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data  |Rsvd |C|E|U|A|P|R|S|F|                                  |
| Offset|     |W|C|R|C|S|S|Y|I|            Window Size           |
|       |     |R|E|G|K|H|T|N|N|                                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Checksum            |         Urgent Pointer        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options (0-40 bytes)                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Field Size Meaning / real-world relevance
Source / Dest Port 16 b each With the IPs, forms the 4-tuple.
Sequence Number 32 b Byte offset of the first data byte in this segment within the stream. On the SYN it carries the ISN (Initial Sequence Number, randomized — see 24.4).
Acknowledgment Number 32 b "I have received every byte up to (ack−1); send me byte ack next." Cumulative. Valid only when ACK flag set (which is always, after the SYN).
Data Offset 4 b Header length in 32-bit words; 5 = 20 bytes, 15 = 60 max. Tells where data starts.
Reserved 3 b Must be 0.
CWR / ECE 1 b each ECN signaling: ECE = "I saw a CE-marked packet, slow down"; CWR = "I reduced my window." Works with the IP-header ECN bits (Ch. 10, 31).
URG + Urgent Pointer 1 b + 16 b "Urgent data" up to the pointer. Effectively dead (Telnet Ctrl-C heritage); often stripped by middleboxes; a minor security footgun.
ACK 1 b Ack number is meaningful. Set on everything except the initial SYN.
PSH 1 b "Push buffered data to the app now." Mostly advisory today.
RST 1 b Reset: abort the connection immediately, no graceful close. Sent for "connection refused" (SYN to a closed port), for data on a nonexistent connection, or by an app calling close() with SO_LINGER 0, or by a firewall forging it.
SYN 1 b Synchronize sequence numbers — connection setup. Consumes one sequence number.
FIN 1 b "I'm done sending." Graceful half-close. Consumes one sequence number.
Window Size 16 b Receive window (rwnd): how many more bytes the sender of this segment is willing to receive right now (flow control, Ch. 30). Max 65535 unless window scaling multiplies it.
Checksum 16 b Covers pseudo-header (src/dst IP, protocol, TCP length) + TCP header + data. Mandatory. NICs usually offload it (Ch. 49) — so a local capture may show "checksum incorrect (offloaded)".
Urgent Pointer 16 b see URG.

23.1 The options that actually matter (negotiated on the SYN)

Option Kind Purpose
MSS 2 Max Segment Size this host will accept (Ch. 8). Each side states its own; effective = min. Typically 1460 (Ethernet) or 1448 (with timestamps).
Window Scale (WScale) 3 Left-shift the 16-bit window by 0–14 bits → windows up to ~1 GB. Must be on both SYNs or scaling is off for the whole connection. Essential for high bandwidth-delay paths (Ch. 35).
SACK-Permitted 4 "I support Selective ACK." Enables SACK blocks in later segments (Ch. 29).
SACK 5 In data segments: "I have bytes A–B and C–D but I'm missing the gap" — lets the sender retransmit only the holes.
Timestamps (TSopt) 8 Two 32-bit values (TSval/TSecr). Used for accurate RTT measurement on every segment (RTTM) and PAWS (Protection Against Wrapped Sequences) on fast links. Costs 12 bytes/segment (10 + 2 padding).
MP_CAPABLE / MP_JOIN 30 Multipath TCP — one logical connection across multiple paths (Wi-Fi + cellular).
TCP Fast Open (TFO) 34 Send data in the SYN on repeat visits (a cookie authorizes it), saving one RTT. Patchy middlebox support.

23.2 Reading flags in a capture

tcpdump shows flags as letters in [ ]:

Symbol Flag
S SYN
S. SYN+ACK (. = ACK)
. ACK only
P. PSH+ACK (data)
F. FIN+ACK (graceful close)
R / R. RST / RST+ACK (abort)
E,W ECE, CWR

Example line:

10:00:00.100 IP 10.0.0.5.51000 > 93.184.216.34.443: Flags [S], seq 1000,
  win 64240, options [mss 1460,sackOK,TS val 111 ecr 0,nop,wscale 7], length 0

23.3 Sequence and acknowledgment arithmetic

  • SYN and FIN each consume one sequence number even though they carry no data. So after SYN seq=1000, the first data byte is seq=1001.
  • A pure ACK carries no data and does not advance the sender's sequence number (which is why a lost pure ACK is simply superseded by the next one — ACKs are not retransmitted).
  • tcpdump -S shows absolute sequence numbers; by default it shows relative ones (starting at 0/1) which is far easier to read.

23.4 War story

A load balancer health check "passed" but real traffic failed. Capture showed the LB's SYN had wscale 7 and sackOK, the backend's SYN-ACK had no options at all — an old middlebox was stripping TCP options on that path. Result: window capped at 64 KB, no SACK; on the ~30 ms link, throughput ceilinged at 65535 / 0.03 ≈ 17 Mbit/s and any loss caused a full timeout. Routing the flow around the option-stripping box restored gigabit. Lesson: always compare the options on both SYNs; a middlebox that mangles them silently kneecaps TCP.

Try it yourself

sudo tcpdump -i any -n -c10 -S 'tcp[tcpflags] & (tcp-syn) != 0'    # all SYNs, abs seq
sudo tcpdump -i any -n 'tcp port 443' -vv | grep -m4 options
# Verify negotiated features for a live socket:
ss -tie dst 93.184.216.34    # shows wscale, sack, ts, cubic, rtt, cwnd, mss...

Generated from real-life-example.md · 8 Oct 2026 16:36