The Runtime Theory
SystemArchitecturenetwork

What happens when you open a TCP connection?

A step-by-step walk of the three-way handshake: SYN, SYN-ACK, ACK, the kernel state machine, and the RTT math that prices every connection.

The Runtime Theory Team1 min read05 steps

trace spine

  1. 01 socket() and connect() syscalls
  2. 02 SYN sent, retransmit timer armed
  3. 03 Server responds with SYN-ACK
  4. 04 ACK completes the handshake
  5. 05 listen backlog and accept

Once the hostname is an address, opening a connection means running the TCP state machine. The three-way handshake is a deliberate, protocol-level round trip — the client cannot send payload bytes until it has paid one RTT. Here is what actually executes.

trace stepApplication

The application calls socket(AF_INET, SOCK_STREAM, 0) — a syscall that returns an fd referencing a freshly allocated kernel struct sock. Then connect(fd, {93.184.216.34:443}, …) enters the kernel. Both are cheap: a few hundred nanoseconds of table allocation, no network activity yet.

trace stepKernel

connect on a stream socket triggers the SYN. The kernel picks the initial sequence number, writes the segment, and hands it to the network stack, which emits the packet. Simultaneously it arms a retransmission timer — the initial RTO, typically 1 second. The socket state transitions SYN_SENT. If the ACK doesn't arrive, the kernel will resend at 1s, 2s, 4s — exponential backoff until ~127s, then ETIMEDOUT.

trace stepKernel

The server's kernel — woken by the packet receive path — validates the SYN, allocates its own socket via the SYN queue, and replies SYN-ACK with its own ISN. The client kernel receives it, verifies the sequence number window, and records the server's ISN plus the measured RTT sample.

trace stepKernel

The client sends the final ACK and transitions to ESTABLISHED. The server does the same on receipt. The handshake has now cost exactly one network round trip. On a LAN that's ~0.2 ms; cross-continent it's 20–100 ms.

bash
sudo tcpdump -ni any 'tcp and port 443'   # look for S, S., .
trace stepApplication

On the server side, the completed socket moves from the SYN queue to the accept queue, and accept() returns the new fd to the application. Only now can read/write transfer bytes — and the first application byte still waits for the next round trip. Total cost to first byte: 2 × RTT from the client's first SYN, or 3 × RTT from when the application started dialing, if DNS was cold.

bash
# RTT measured by the kernel — visible after connect:
ss -i | grep -E "rtt|state"

The measurable budget: socket + connect ≈ 1–3 µs of CPU, the SYN-ACK exchange ≈ 1 RTT of wall clock, and everything after is application-level. If you ever see a connection stall at "SYN_SENT" for seconds, you're watching the backoff timer, not the network — the kernel will retry for about two minutes before it gives up.