Establishing a Reliable Connection
TCP (Transmission Control Protocol) provides a reliable, ordered, byte-stream connection between two applications. Before any data can flow, both sides must agree on initial parameters through a three-way handshake. This handshake synchronizes sequence numbers, confirms both sides are reachable, and establishes the connection state on both hosts.
The Three-Way Handshake
The connection establishment involves three segments, each identified by flags in the TCP header:
Step 1 — SYN (Client to Server): The client sends a TCP segment with the SYN (Synchronize) flag set. This segment carries the client's Initial Sequence Number (ISN) — a 32-bit value that will be the starting point for numbering all bytes the client sends. The segment has no payload. Example: SYN, Seq=1000.
Step 2 — SYN-ACK (Server to Client): The server responds with both the SYN and ACK flags set. The SYN carries the server's own ISN. The ACK acknowledges the client's ISN by setting the acknowledgment number to client's ISN + 1. Example: SYN-ACK, Seq=5000, Ack=1001.
Step 3 — ACK (Client to Server): The client sends a segment with the ACK flag set, acknowledging the server's ISN (Ack = server's ISN + 1). At this point, the connection is ESTABLISHED on both sides. This segment can optionally carry data. Example: ACK, Seq=1001, Ack=5001.
After these three segments, both sides know each other's starting sequence numbers and can begin sending data.
Initial Sequence Numbers (ISN)
The ISN is not zero or one — it is randomized for security. Early TCP implementations used a predictable ISN (incrementing by a fixed amount per connection), which enabled TCP sequence prediction attacks: an attacker could forge packets with guessed sequence numbers, hijacking connections. Modern operating systems generate ISNs using cryptographic random number generators, making prediction computationally infeasible.
Connection Teardown (4-Way Close)
TCP uses a four-way handshake to close a connection gracefully, because each direction is closed independently:
- FIN — the initiator sends a segment with the FIN flag, signaling "I have no more data to send."
- ACK — the receiver acknowledges the FIN.
- FIN — the receiver sends its own FIN when it is also done sending.
- ACK — the initiator acknowledges the receiver's FIN.
A simultaneous close occurs when both sides send FIN at roughly the same time. Both transition through the CLOSING state and converge to TIME_WAIT.
TIME_WAIT State
After sending the final ACK, the connection enters the TIME_WAIT state for a duration of 2 * MSL (Maximum Segment Lifetime), typically 60-120 seconds. This serves two purposes:
- Ensures the final ACK is received. If the remote side's FIN was retransmitted (because the ACK was lost), the local side can resend the ACK.
- Prevents old segments from a prior connection from being confused with a new one. By waiting 2*MSL, all segments from the old connection will have expired.
On busy servers, thousands of connections in TIME_WAIT can consume ports and memory. The SO_REUSEADDR socket option mitigates this by allowing a new connection to bind to a port still in TIME_WAIT.
SYN Flood Attack and SYN Cookies
A SYN flood is a denial-of-service attack where the attacker sends thousands of SYN segments with spoofed source IPs. The server allocates state for each half-open connection (the SYN queue), eventually exhausting memory and preventing legitimate connections.
The defense is SYN cookies: instead of storing state for the half-open connection, the server encodes the connection parameters (ISN, MSS, timestamp) into the SYN-ACK's sequence number using a cryptographic hash. When the client's ACK returns, the server reconstructs the state from the acknowledged sequence number. This allows the server to handle SYN floods without maintaining any per-connection state until the handshake completes.
Real-Life: What Happens When You Open a Web Page
When your browser connects to example.com:443 (HTTPS), the TCP handshake is the very first thing that happens — before any TLS negotiation, before any HTTP request:
Packet 1 (Client -> Server): SYN, Seq=28394710 (random ISN), Window=65535, MSS=1460. The client proposes a maximum segment size of 1460 bytes (Ethernet MTU 1500 minus 20 bytes IP header minus 20 bytes TCP header).
Packet 2 (Server -> Client): SYN-ACK, Seq=839201455 (server's random ISN), Ack=28394711, Window=28960, MSS=1460. The server acknowledges the client's ISN and proposes its own.
Packet 3 (Client -> Server): ACK, Seq=28394711, Ack=839201456. Connection ESTABLISHED. Total time: 1 RTT (round-trip time) from the client's perspective — the client can send data immediately after this packet.
After the handshake: The TLS handshake begins (another 1-2 RTT), then the HTTP request is sent. This is why TCP+TLS to a server 100ms away takes at least 200-300ms before any actual page data flows — and why protocols like QUIC (which combines transport and encryption handshakes) are faster.
Connection teardown example: When the browser finishes loading the page and the server sends all data:
- Server: FIN, Seq=839300000, Ack=28395200
- Client: ACK, Seq=28395200, Ack=839300001
- Client: FIN, Seq=28395200, Ack=839300001
- Server: ACK, Seq=839300001, Ack=28395201
The client enters TIME_WAIT for ~60 seconds. If you run netstat on a busy web server, you will see hundreds of connections in TIME_WAIT — this is normal.