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.
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.
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.
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.
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.
sudo tcpdump -ni any 'tcp and port 443' # look for S, S., .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.
# 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.