Loading content...
The three-way handshake does more than establish sequence numbers—it's a capability discovery mechanism that shapes how the connection behaves for its entire lifetime. Every TCP option, every parameter, every performance characteristic is locked in during these three segments.
Modern TCP connections use sophisticated features like window scaling for high-bandwidth paths, selective acknowledgments for efficient loss recovery, and timestamps for accurate RTT measurement. But none of these work unless both endpoints agree during the handshake. Miss the negotiation window, and you're stuck with 1985-era TCP capabilities.
By the end of this page, you will understand how Maximum Segment Size is determined and why it matters, window scaling negotiation for high-bandwidth networks, SACK enablement for efficient loss recovery, timestamp option for RTT and PAWS, ECN negotiation for congestion signaling, and how parameter mismatches affect performance.
TCP was designed in 1981 when networks were slow (kilobits), lossy, and simple. Over decades, TCP has evolved through backward-compatible extensions—optional features that enhance performance when both endpoints support them.
The Negotiation Challenge:
How do two endpoints discover each other's capabilities without a pre-arranged agreement? The solution: option fields in the TCP header. Each SYN and SYN-ACK carries optional capability advertisements. If both sides include an option, the feature is enabled. If either omits it, the feature is unavailable.
Why This Design?
Backward Compatibility — A new client with advanced options can still connect to an old server. The old server ignores unknown options and responds without them.
Path Transparency — Middleboxes (firewalls, NATs) may alter or strip options. Negotiation happens end-to-end with whatever survives.
Flexibility — Different connections can negotiate different capabilities based on endpoint support and path characteristics.
| Capability | If Negotiated | If Missing | Performance Impact |
|---|---|---|---|
| Window Scaling | Windows up to 1 GB | Max 64 KB window | Catastrophic on high-BW×delay paths |
| SACK | Selective retransmission | Go-back-N on loss | Major on lossy networks |
| Timestamps | Accurate RTT, PAWS | Coarse RTT estimation | Moderate to significant |
| Proper MSS | No fragmentation | IP fragmentation possible | Moderate (fragmentation overhead) |
| ECN | Router congestion signals | Only loss-based signals | Minimal to moderate |
There is no mechanism to renegotiate capabilities mid-connection. Whatever is agreed (or not) during the handshake applies for the connection's entire lifetime. If SACK wasn't enabled in the SYN/SYN-ACK exchange, you cannot add SACK later—even if both endpoints support it.
The Maximum Segment Size (MSS) option is arguably the most important parameter negotiated during the handshake. It specifies the largest TCP segment (data portion) that an endpoint is willing to receive.
Why MSS Matters:
TCP segments are encapsulated in IP datagrams. If a segment is too large, the IP datagram will be fragmented—broken into smaller pieces that are reassembled at the destination. Fragmentation is extremely undesirable:
MSS Calculation:
MSS = MTU - IP_Header - TCP_Header
For standard Ethernet with IPv4:
MSS = 1500 - 20 - 20 = 1460 bytes
For Ethernet with IPv6:
MSS = 1500 - 40 - 20 = 1440 bytes
| Network Type | MTU | IPv4 MSS | IPv6 MSS |
|---|---|---|---|
| Ethernet | 1500 | 1460 | 1440 |
| Jumbo Frames | 9000 | 8960 | 8940 |
| PPPoE | 1492 | 1452 | 1432 |
| VPN Overhead | ~1400 | ~1360 | ~1340 |
MSS Negotiation Mechanics:
Key Insight: MSS is per-direction. The client might send MSS=1460 while server sends MSS=1400. The client then sends segments ≤1400, and server sends segments ≤1460.
Path MTU Discovery (PMTUD):
MSS from the handshake assumes local interface MTU, but the path between endpoints may have lower MTU (e.g., VPN tunnels). Path MTU Discovery:
PMTUD adjusts MSS after the handshake, but initial MSS from negotiation is the starting point.
If MSS option is missing from SYN or SYN-ACK, TCP assumes a conservative default of 536 bytes (576-byte minimum MTU minus 40 bytes headers). This severely limits performance. All modern implementations include MSS in every SYN—there's no reason to omit it.
The original TCP header provides only 16 bits for window size, limiting the maximum receive window to 65,535 bytes. For modern high-bandwidth, high-latency networks, this is catastrophically insufficient.
The Bandwidth-Delay Product Problem:
Maximum Throughput = Window Size / RTT
With 64KB window and 100ms RTT:
Max Throughput = 65,535 bytes / 0.1s = 655,350 bytes/s ≈ 5.2 Mbps
A 100ms RTT path with 64KB window is capped at 5.2 Mbps—unusable for modern networks. A 1 Gbps link with 100ms RTT needs:
Required Window = 1 Gbps × 0.1s = 100 Mbit = 12.5 MB
Window scaling (RFC 7323) extends the window to up to 1 GB.
How Window Scaling Works:
The Window Scale option specifies a shift count (0-14). The actual window is:
Actual_Window = Window_Field × 2^(Scale_Factor)
Examples:
| Scale Factor | Multiplier | Max Window | Use Case |
|---|---|---|---|
| 0 | ×1 | 64 KB | Compatibility fallback |
| 4 | ×16 | 1 MB | LAN environments |
| 7 | ×128 | 8 MB | Standard internet |
| 10 | ×1024 | 64 MB | High-BDP paths |
| 14 | ×16384 | 1 GB | Maximum possible |
Window Scale Negotiation Rules:
Both sides must participate — If client includes Window Scale but server doesn't respond with Window Scale, the feature is disabled for the entire connection
Each side chooses its own scale — Client might use scale=7, server might use scale=5. They don't need to match.
Scale applies to advertised windows — When client advertises window with scale=7, server multiplies the window field by 128 to get actual receive window
Scale is fixed for connection lifetime — Cannot change after handshake
Negotiation Example:
Client SYN: Window=65535, Window_Scale=7
Server SYN-ACK: Window=32768, Window_Scale=5
Client's actual receive window: 65535 × 128 = 8,388,480 bytes
Server's actual receive window: 32768 × 32 = 1,048,576 bytes
Some broken middleboxes (old firewalls, NATs) don't understand Window Scale and strip it from SYN-ACK. The client thinks scaling is enabled, but the server's unscaled window is misinterpreted as huge. This causes data to pile up beyond the real buffer, eventually timing out. Diagnosing requires packet capture on both sides.
Standard TCP acknowledgments are cumulative—they acknowledge all bytes up to a sequence number but can't specify gaps. When packet 3 of 5 is lost, the receiver can only say "I have bytes up to packet 2." The sender doesn't know if packets 4 and 5 arrived.
The Problem with Cumulative ACKs:
Sent: [1][2][3][4][5]
Received: [1][2][ ][4][5] (Packet 3 lost)
ACK: "I have up to sequence 2001"
Sender knows packet 3 is missing, but:
- Did 4 and 5 arrive?
- Should I retransmit only 3, or 3, 4, and 5?
Without SACK, TCP often retransmits more than necessary (Go-Back-N behavior) or uses complex algorithms (NewReno, etc.) to probe what was received.
Selective Acknowledgments (SACK):
SACK (RFC 2018) lets receivers explicitly report which non-contiguous blocks were received:
ACK: "Cumulative ACK for 2001; SACK blocks: [4001-5000], [6001-7000]"
Now the sender knows:
Retransmit only the missing ranges: major efficiency gain.
SACK Negotiation:
SACK Negotiation Success:
If both SYN and SYN-ACK contain SACK-Permitted:
If SACK Not Negotiated:
SACK support is nearly universal today (Linux, Windows, macOS, iOS, Android all support it). However, some legacy or embedded systems may not include SACK-Permitted. When diagnosing poor performance after packet loss, check whether SACK was negotiated—its absence dramatically worsens recovery.
The TCP Timestamps option (RFC 7323) provides two critical capabilities: accurate RTT measurement and Protection Against Wrapped Sequences (PAWS).
Timestamps Option Structure:
The option contains two 32-bit values:
+-------+-------+-------------------+-------------------+
| Kind | Len | TSval (4 bytes) | TSecr (4 bytes) |
| 8 | 10 | Sender's time | Echo of peer's |
+-------+-------+-------------------+-------------------+
RTT Measurement:
Timestamps enable per-acknowledgment RTT calculation:
Without timestamps, RTT can only be measured when retransmission timer is used—coarse and infrequent. Timestamps enable fine-grained, continuous RTT tracking.
Protection Against Wrapped Sequences (PAWS):
On very fast connections (10+ Gbps), the 32-bit sequence space can wrap in seconds:
10 Gbps = 1.25 GB/s → 4 GB (sequence space) wraps in ~3.4 seconds
An old duplicate packet with sequence X might arrive after the sequence has wrapped past X again. Without PAWS, TCP might accept this old data as new.
PAWS uses timestamps to reject such duplicates:
Timestamps Negotiation:
If both sides include Timestamps in their handshake segments, timestamps are enabled. If either omits them, timestamps are disabled for the connection.
Timestamps in SYN Example:
1234567891011121314
SYN: TCP Option - Timestamps TSval: 123456789 TSecr: 0 ← No previous timestamp to echo SYN-ACK: TCP Option - Timestamps TSval: 987654321 ← Server's timestamp TSecr: 123456789 ← Echoes client's SYN timestamp ACK: TCP Option - Timestamps TSval: 123456802 ← Client's new timestamp TSecr: 987654321 ← Echoes server's SYN-ACK timestampEach segment with timestamps carries 10 extra bytes (12 with padding). For small segments, this is non-negligible overhead. However, the benefits (RTT measurement, PAWS) justify the cost for most connections. High-frequency trading systems sometimes disable timestamps to minimize latency.
Traditional TCP detects congestion only through packet loss—by the time packets are dropped, the network is already severely congested. Explicit Congestion Notification (ECN) allows routers to signal congestion before dropping packets.
How ECN Works:
Result: Congestion control without packet loss—lower latency, less retransmission.
ECN Negotiation During Handshake:
ECN is negotiated using two TCP flags: ECE (ECN-Echo) and CWR (Congestion Window Reduced).
SYN (Client):
Flags: SYN, ECE, CWR
Setting both ECE and CWR in SYN indicates "I support ECN."
SYN-ACK (Server):
Flags: SYN, ACK, ECE (if ECN supported)
or
Flags: SYN, ACK (if ECN not supported)
Server responds with ECE only (not CWR) to indicate ECN support.
| Client SYN Flags | Server SYN-ACK Flags | ECN Status |
|---|---|---|
| SYN, ECE, CWR | SYN, ACK, ECE | ECN Enabled ✓ |
| SYN, ECE, CWR | SYN, ACK | ECN Disabled (server doesn't support) |
| SYN | SYN, ACK, ECE | ECN Disabled (client didn't request) |
| SYN | SYN, ACK | ECN Disabled (neither supports) |
ECN Benefits:
ECN Challenges:
ECN was historically disabled due to middlebox issues, but support has improved dramatically. Apple, Google, and Microsoft now enable ECN by default on many platforms. Modern data centers use ECN extensively for DCTCP (Data Center TCP). Check ECN negotiation status when debugging latency-sensitive applications.
TCP options must fit within the header's size constraints and follow encoding rules. Understanding these mechanics helps diagnose option-related issues.
TCP Header Size Limit:
The Data Offset field is 4 bits, specifying header length in 32-bit words:
Common Option Sizes:
| Option | Size (bytes) | Cumulative in Typical SYN |
|---|---|---|
| MSS | 4 | 4 |
| SACK Permitted | 2 | 6 |
| Timestamps | 10 | 16 |
| NOP (padding) | 1 | 17 |
| Window Scale | 3 | 20 |
| NOP + NOP (alignment) | 2 | 22 |
| End of Option List | 1 | Varies |
Option Encoding Details:
Options follow a Type-Length-Value (TLV) format:
+------+--------+-----------------+
| Kind | Length | Data |
+------+--------+-----------------+
1B 1B (Length-2) B
Exceptions:
Alignment Requirements:
Options don't require specific alignment, but implementations often pad to 32-bit boundaries for efficiency. NOP (0x01) is inserted as needed:
Actual option bytes: [MSS:4][WSc:3][SACK:2][TS:10] = 19 bytes
With padding: [MSS:4][NOP:1][WSc:3][SACK:2][NOP:1][NOP:1][TS:10] = 22 bytes
Option Processing Rules:
SYN Cookie Option Limitations:
When SYN cookies are active, the server doesn't store client options. The cookie can only encode:
If the connection completes with cookies, these options may not be properly enabled. Modern implementations activate cookies only under attack, minimizing impact.
Some middleboxes strip or modify options they don't understand. Timestamps might be removed for 'security,' Window Scale might be stripped by old NATs. If performance is worse than expected, capture packets on both sides of potential middleboxes to see if options survive the path.
Let's examine real-world scenarios where parameter negotiation has dramatic effects on performance.
Scenario 1: Transcontinental High-Bandwidth Connection
Path: New York to Tokyo
RTT: 200ms
Bandwidth: 1 Gbps
Bandwidth-Delay Product: 1 Gbps × 0.2s = 25 MB
Scenario 2: Lossy Wireless Network
Path: Mobile device to cloud server
Loss rate: 2%
RTT: 80ms
Scenario 3: High-Frequency Data Center Traffic
Path: In-rack server communication
RTT: 0.1ms
Bandwidth: 25 Gbps
| Parameter | Impact | Recommendation |
|---|---|---|
| MSS | Jumbo frames (9000 MTU) reduce header overhead significantly | Use MSS ~8960 if path supports |
| Window Scale | Low RTT means low BDP; scaling less critical | Moderate scale factor sufficient |
| Timestamps | Add latency overhead (12 bytes/segment) | May disable for ultra-low-latency |
| ECN | Critical for data center congestion control (DCTCP) | Enable; most DC switches support |
There's no universal 'best' configuration. High-BDP paths need aggressive window scaling. Lossy networks need SACK. Data centers need ECN. Low-latency trading may disable timestamps. Analyze your path characteristics and tune accordingly.
When TCP performance is poor, parameter negotiation issues are a common culprit. Here's how to diagnose them.
Capture and Analyze the Handshake:
Using tcpdump or Wireshark, capture the SYN and SYN-ACK:
tcpdump -i any 'tcp[tcpflags] & (tcp-syn) != 0' -vvv
Look for:
1234567891011
SYN: Flags [S], seq 1234567890, win 65535, options [mss 1460,sackOK,TS val 123 ecr 0,nop,wscale 7] SYN-ACK: Flags [S.], seq 9876543210, ack 1234567891, win 65535, options [mss 1460,sackOK,TS val 456 ecr 123,nop,wscale 8] Analysis:✓ MSS 1460 both sides (Ethernet compatible)✓ SACK Permitted both sides (selective ACKs enabled)✓ Timestamps both sides (RTT measurement, PAWS enabled)✓ Window Scale both sides (7 and 8 - large windows supported)1234567891011
SYN: Flags [S], seq 1234567890, win 65535, options [mss 1460,sackOK,TS val 123 ecr 0,nop,wscale 7] SYN-ACK: Flags [S.], seq 9876543210, ack 1234567891, win 8192, options [mss 1460] Analysis:✗ No SACK in SYN-ACK (loss recovery will be poor)✗ No Timestamps in SYN-ACK (RTT estimation degraded)✗ No Window Scale in SYN-ACK (stuck at 8KB window!)→ Either server is very old, or middlebox stripped optionsCommon Issues and Solutions:
| Symptom | Likely Cause | Solution |
|---|---|---|
| Window Scale in SYN, not in SYN-ACK | Old server or middlebox stripping | Update server; check NAT/firewall |
| MSS mismatch causes fragmentation | Path MTU lower than local MTU | Enable PMTUD; reduce MSS |
| SACK not negotiated | Server doesn't support SACK | Update server TCP stack |
| Timestamps missing one direction | Asymmetric path, different middleboxes | Check both directions separately |
| Very small MSS (536) | MSS option stripped or missing | Check for middlebox interference |
Always capture packets on both client and server when debugging. This reveals whether options are being stripped mid-path. If client's SYN shows Window Scale but server never receives it, something in between is modifying packets.
We've thoroughly examined how TCP endpoints negotiate capabilities during the three-way handshake, and how these negotiated parameters shape connection behavior. Let's consolidate the essential knowledge:
Module Complete:
With this page, we've completed our deep dive into the TCP three-way handshake. From the initial SYN that proposes a connection, through the SYN-ACK that responds and synchronizes, to the final ACK that establishes the channel—and the parameter negotiation that shapes its behavior—you now understand this fundamental protocol mechanism at a deep, operational level.
This knowledge enables you to debug connection issues, optimize performance, understand security implications, and appreciate why TCP behaves as it does in production environments.
You've mastered the TCP three-way handshake—from individual segments (SYN, SYN-ACK, ACK) through complete connection establishment to parameter negotiation. This foundational knowledge prepares you for understanding TCP's data transfer, flow control, congestion control, and connection termination mechanisms.