TCP Three-Way Handshake
SYN-ACK: The Synchronize-Acknowledge Segment
Read aloud is unavailable in this browser.
The Server Responds: Acknowledgment and Synchronization in One
The SYN-ACK segment is a masterpiece of protocol efficiency. In a single packet, the server accomplishes two critical objectives: acknowledging the client's connection request and proposing its own sequence number for the reverse direction. This dual-purpose design reduces Round-Trip Time (RTT) overhead by combining what could have been two separate messages.
When a server receives a valid SYN, it responds with the SYN-ACK—a segment that says, in essence: "I received your request to connect (ACK), and here is my initial sequence number for data I'll send to you (SYN)." This elegant combination makes TCP's connection establishment complete in just three packets rather than four.
By the end of this page, you will understand the dual purpose of the SYN-ACK segment, how the server selects its Initial Sequence Number, how acknowledgment numbers work in TCP, the option negotiation response process, server-side state transitions, and edge cases like SYN-ACK retransmission.
The Dual Nature of SYN-ACK
The SYN-ACK segment has two simultaneous meanings, encoded in two flags that are both set:
1. ACK (Acknowledgment) — "I received your SYN"
The ACK flag being set indicates that the Acknowledgment Number field is valid. This value tells the client: "I received your SYN with ISN = X, so I expect your next byte to have sequence number X + 1."
2. SYN (Synchronize) — "Here is my ISN"
The SYN flag being set indicates that the Sequence Number field contains the server's Initial Sequence Number (ISN). This tells the client: "When I start sending you data, it will begin at sequence number Y."
TCP could have used a separate ACK packet followed by a separate SYN packet from the server. But network communication is expensive—every packet incurs processing, latency, and header overhead. Combining SYN+ACK into one segment halves the number of packets needed for the server's response, reducing latency from 2 RTT to 1.5 RTT for connection establishment.
The Two-Way Handshake Fallacy:
A common misconception is that TCP could work with just two packets: SYN from client, SYN-ACK from server. Why isn't this sufficient?
Consider: after the server sends SYN-ACK, it knows the client wants to connect, but the client hasn't confirmed it received the server's response. The server's sequence number hasn't been acknowledged. Without the final ACK:
- The server doesn't know if its SYN-ACK was received
- The server's ISN hasn't been acknowledged
- The client could have crashed, been firewalled, or changed its mind
- The server would enter ESTABLISHED state prematurely
The three-way handshake ensures both sides have confirmed both directions of communication are functional.
| Aspect | Two-Way (Hypothetical) | Three-Way (Actual TCP) |
|---|---|---|
| Packets required | 2 | 3 |
| Client confirms server's ISN | No | Yes (final ACK) |
| Server confirms client received SYN-ACK | No | Yes |
| Vulnerable to half-open connections | Highly | Protected |
| Reliable bidirectional setup | No | Yes |
Understanding the Acknowledgment Number
The Acknowledgment Number in the SYN-ACK is deceptively simple in concept but critical in practice. When the server sets ACK = client_ISN + 1, it's making a precise statement about what it expects next.
Acknowledgment Number Semantics:
The TCP acknowledgment number indicates the next sequence number the receiver expects. It implicitly acknowledges all bytes up to (but not including) this value.
In the SYN-ACK:
Acknowledgment Number = Client's ISN + 1
This means:
- The server has received everything up to and including the client's SYN
- The SYN itself 'consumed' one sequence number (the ISN)
- The next byte the client sends should have sequence number ISN + 1
Even though the SYN segment carries no application data, it occupies one sequence number. This is intentional: by consuming a sequence number, the SYN can be reliably acknowledged. The acknowledgment 'ISN + 1' unambiguously means 'I received your SYN.' Without this, there would be no way to confirm SYN receipt distinctly from data receipt.
Example Acknowledgment Calculation:
Suppose the client sends a SYN with:
Sequence Number (ISN): 1000
The server's SYN-ACK will contain:
Acknowledgment Number: 1001
This tells the client: "I've received everything through sequence 1000 (including your SYN). Send byte 1001 whenever you're ready."
When the client later sends its first data segment, it will begin:
Sequence Number: 1001
Data: "GET / HTTP/1.1\r
..."
Cumulative Acknowledgment:
TCP uses cumulative acknowledgments—the acknowledgment number indicates all contiguous bytes up to that point have been received. This is important for the handshake:
- The SYN-ACK's ACK = ISN + 1 acknowledges the SYN
- Later data ACKs acknowledge all bytes through the ACK number
- Gaps in received data don't get acknowledged until filled (without SACK)
The Server's Initial Sequence Number
Just as the client proposed its ISN in the SYN, the server must propose its own ISN in the SYN-ACK. This server ISN follows the same security requirements and generation methods as the client's ISN, but the values are completely independent.
Why Independent ISNs?
TCP connections are full-duplex—both sides can send data simultaneously. Each direction has its own sequence number space:
- Client → Server: Uses client's ISN as the starting point
- Server → Client: Uses server's ISN as the starting point
These two sequence spaces are entirely separate. The client's acknowledgments reference the server's sequence space, and vice versa.
- •ISN: 1000 (from SYN)
- •First data byte: 1001
- •Referenced by server's ACKs
- •Tracks bytes sent client → server
- •ISN: 5000 (from SYN-ACK)
- •First data byte: 5001
- •Referenced by client's ACKs
- •Tracks bytes sent server → client
Server ISN Generation:
The server generates its ISN using the same cryptographic randomization method as the client (RFC 6528):
Server_ISN = M + F(server_ip, server_port, client_ip, client_port, secret_key)
Note the parameters are the same tuple but with consistent ordering. The function F uses a secret key known only to the host, ensuring:
- Unpredictability to attackers — Can't guess the server's ISN before receiving SYN-ACK
- Different from client's ISN — The hash function with different secret keys produces different values
- Consistent per connection — Same socket pair yields same offset, helping avoid old duplicate issues
If an attacker can observe a server's ISNs across multiple connections, they might detect patterns that reveal the secret key or generation algorithm weaknesses. Modern implementations use strong cryptographic functions (like MD5 or SHA-based HMACs) and periodically rotate secret keys to prevent this.
Complete Structure of the SYN-ACK Segment
Let's examine every field in a typical SYN-ACK segment, comparing it to the original SYN to understand the response pattern:
| Field | Size | SYN-ACK Value | Explanation |
|---|---|---|---|
| Source Port | 16 bits | Service port (e.g., 443) | Server's well-known or registered port |
| Destination Port | 16 bits | Ephemeral port (e.g., 54321) | Echoes client's source port |
| Sequence Number | 32 bits | Server's ISN (e.g., 0x5A6B7C8D) | Server's Initial Sequence Number |
| Acknowledgment Number | 32 bits | Client_ISN + 1 | Acknowledges client's SYN |
| Data Offset | 4 bits | 5-15 (in 32-bit words) | Header length; typically 10+ with options |
| Reserved | 4 bits | 0 | Reserved for future use |
| Flags | 8 bits | 0x12 (SYN + ACK) | Both SYN and ACK flags set |
| Window Size | 16 bits | Server's receive window | Server's available buffer space |
| Checksum | 16 bits | Computed value | Integrity check including pseudo-header |
| Urgent Pointer | 16 bits | 0 (ignored) | Not valid when URG flag not set |
| Options | Variable | Server's supported options | Response to client's capability negotiation |
Critical Observations:
-
Flags = 0x12 — In the 8-bit flags field, bit 1 (SYN) and bit 4 (ACK) are set: 0001 0010 = 0x12
-
Port Reversal — Source and destination ports are swapped from the SYN. The server responds from its service port to the client's ephemeral port.
-
Both Sequence and Acknowledgment Valid — Unlike the pure SYN, both fields carry meaningful values. The sequence number is the server's ISN; the acknowledgment number references the client's sequence space.
-
Server's Window — The window size advertises how much data the server can receive. This may differ significantly from the client's advertised window.
Just like the client's SYN, the server's SYN-ACK consumes one sequence number. When the client sends its final ACK, it will acknowledge the server's ISN + 1, confirming receipt of the SYN-ACK. If the server's ISN is 5000, the client's ACK will contain ACK = 5001.
Option Negotiation in the SYN-ACK
The SYN-ACK is the server's opportunity to accept, modify, or reject the optional capabilities the client proposed. The negotiation follows specific rules:
Negotiation Semantics:
| Client Proposes | Server Response | Outcome |
|---|---|---|
| MSS = 1460 | MSS = 1460 | Both agree on 1460 |
| MSS = 1460 | MSS = 1400 | Server restricts to 1400 |
| MSS = 1460 | No MSS option | Default 536 used |
| Window Scale = 7 | Window Scale = 5 | Each side uses its own scale |
| Window Scale = 7 | No Window Scale | Large windows disabled |
| SACK Permitted | SACK Permitted | SACK enabled |
| SACK Permitted | No SACK option | SACK disabled |
| Timestamps | Timestamps | RTT measurement enabled |
| Timestamps | No Timestamps | Timestamps disabled |
Option-by-Option Analysis:
Maximum Segment Size (MSS): Each side independently declares the largest segment it can receive. The effective MSS for each direction is what that direction's receiver advertised. If client says 1460 and server says 1400:
- Client sends segments with max data payload: 1400 bytes
- Server sends segments with max data payload: 1460 bytes
Window Scale: Unlike MSS, Window Scale must be present in both SYN and SYN-ACK to be enabled. If either omits it, neither side can use scaled windows. Each side's scale factor applies to windows they advertise, not receive.
SACK Permitted: This is a binary negotiation. If both include SACK Permitted, selective acknowledgments can be used. If either omits it, SACK is unavailable for the connection.
Timestamps: If both sides include the Timestamps option, subsequent segments will carry timestamps for RTT estimation and PAWS. The server echoes the client's timestamp in the echo-reply field.
Whatever options are agreed upon in the handshake remain fixed for the entire connection. There is no mechanism to add capabilities mid-connection. This is why both SYN and SYN-ACK typically include all supported options—missing the negotiation window means missing the capability entirely.
Server's Option Strategy:
Servers must balance compatibility with performance:
- Always include MSS — Without it, 536-byte segments limit throughput
- Always include Window Scale — Essential for high-bandwidth paths
- Always include SACK Permitted — Critical for loss recovery
- Include Timestamps if client does — Enables RTT measurement, helps with PAWS
Servers may deliberately omit options in constrained environments:
- Embedded systems might omit Window Scale to save buffer memory
- Security appliances might strip Timestamps to prevent timing attacks
- Middleboxes might not support certain options
Server State Transition: LISTEN → SYN_RECEIVED
When the server sends the SYN-ACK, it undergoes a critical state transition in the TCP state machine. Understanding this transition is essential for debugging connection issues and understanding resource management.
State Transition Sequence:
- •LISTEN State — Server is waiting for connection requests (application called
listen()) - •SYN Received — Valid SYN arrives on a listening socket
- •SYN-ACK Sent — Server constructs and transmits SYN-ACK
- •SYN_RECEIVED State — Server now in SYN_RECEIVED, waiting for client's ACK
- •ACK Received — Client's final handshake ACK arrives
- •ESTABLISHED State — Connection fully open, data transfer can begin
SYN_RECEIVED State Characteristics:
In SYN_RECEIVED, the connection is half-open:
- Server has allocated connection state (TCB)
- Server's ISN has been proposed but not acknowledged
- Server is waiting for client's ACK to complete handshake
- Retransmission timer is running (will resend SYN-ACK if no ACK arrives)
- Connection cannot transfer data yet
Timeout Behavior:
If the client's ACK never arrives, the server's SYN-ACK retransmission timer expires:
- First timeout (~3 seconds): Retransmit SYN-ACK
- Subsequent timeouts: Exponential backoff (6s, 12s, 24s, etc.)
- Maximum retries reached: Connection aborted, resources freed
Typical Linux defaults: 5 retries with exponential backoff, totaling ~31 seconds before giving up.
A server in SYN_RECEIVED has committed resources (memory for TCB, entry in connection table) to a connection that isn't complete. An attacker sending SYN from spoofed addresses never intends to complete the handshake, leaving the server with many half-open connections consuming resources. This is the SYN flood attack, which SYN cookies were designed to counter.
SYN-ACK Retransmission and Loss Recovery
Network packets can be lost. When the client's original SYN triggers a SYN-ACK that never reaches the client, or when the client's ACK never reaches the server, retransmission mechanisms ensure the handshake can still complete.
Scenario 1: SYN-ACK Lost (Never Reaches Client)
| Time | Client State | Server State | Action |
|---|---|---|---|
| T=0 | SYN_SENT | LISTEN | Client sends SYN |
| T=50ms | SYN_SENT | SYN_RECEIVED | Server sends SYN-ACK (lost in transit) |
| T=3s | SYN_SENT | SYN_RECEIVED | Server's timer expires, retransmits SYN-ACK |
| T=3.05s | ESTABLISHED* | SYN_RECEIVED | Client receives SYN-ACK, sends ACK |
| T=3.1s | ESTABLISHED | ESTABLISHED | Server receives ACK, handshake complete |
Scenario 2: Client's ACK Lost (Server Never Gets ACK)
| Time | Client State | Server State | Action |
|---|---|---|---|
| T=0 | SYN_SENT | LISTEN | Client sends SYN |
| T=50ms | SYN_SENT | SYN_RECEIVED | Server sends SYN-ACK |
| T=100ms | ESTABLISHED | SYN_RECEIVED | Client receives SYN-ACK, sends ACK (lost) |
| T=3s | ESTABLISHED | SYN_RECEIVED | Server's timer expires, retransmits SYN-ACK |
| T=3.05s | ESTABLISHED | SYN_RECEIVED | Client receives duplicate SYN-ACK, retransmits ACK |
| T=3.1s | ESTABLISHED | ESTABLISHED | Server receives ACK, handshake complete |
Key Observations:
-
Client may enter ESTABLISHED before server — The client considers the connection established upon receiving SYN-ACK, before its ACK is delivered or processed.
-
Duplicate SYN-ACKs are handled gracefully — The client responds to duplicate SYN-ACKs by retransmitting its ACK. This is idempotent and safe.
-
Exponential backoff limits network load — Retransmissions don't flood the network; they're spaced with increasing intervals.
-
Connection eventually times out — If repeated retransmissions fail, the server abandons the connection attempt.
Interestingly, some TCP implementations allow the client to send data immediately after sending its ACK (piggybacked with the ACK or in a following segment), even before receiving any confirmation from the server. This data will be accepted once the server completes the handshake. TCP Fast Open (TFO) takes this further by allowing data in the SYN itself.
KEEP LEARNING
There’s more to understand.
You’re reading the introduction. Create a free account to read the complete lesson, save notes and keep track of your progress.