KomuraSoft LLC
Chapter 2

Connections — TCP's three-way handshake and teardown

A connection is really just both ends agreeing where their numbering starts. Follow why SYN → SYN/ACK → ACK needs exactly three packets, and what FIN and TIME_WAIT protect.

What does "opening a connection" really mean?

Day to day, "if connect() returns, we're connected" is good enough. But during an incident, you cannot move forward unless you can tell apart what "cannot connect" actually means — was it refused, was there no response, or did it drop after establishment? For that you need to know what "opening a connection" physically is.

The answer is surprisingly plain. No dedicated line is reserved along the path, and the routers in between remember nothing. A connection is nothing more than a pair of ledgers, one in each end's memory, recording "with the peer of this 4-tuple, up to which number have we exchanged data". So the ritual of creating a connection has exactly one job — tell each other the number we will start counting from, and confirm we each heard it.

This starting number is called the initial sequence number (ISN). The reason we do not start counting from 1 connects to TIME_WAIT in question 5 — to avoid confusion with old packets from a connection that used the same 4-tuple just moments ago, each side starts from a hard-to-predict value every time.

Sequence diagram of the three packets SYN, SYN/ACK, and ACK traveling between client and server, with both initial sequence numbers confirmed and the state becoming ESTABLISHED

The three-way handshake. The example uses ISN 5000 for the client and 9000 for the server. Verify that the ack number is always 'the peer's number + 1 = the next number wanted.'

Reading the three packets one by one

We follow an example with the client's ISN at 5000 and the server's ISN at 9000.

1. SYN (client → server)
seq = 5000. "I want to connect. I start counting from 5000." A SYN carries no data but consumes one number
2. SYN/ACK (server → client)
seq = 9000, ack = 5001. "I heard up to 5001 (= I received your SYN). I start counting from 9000" — confirmation and declaration riding together
3. ACK (client → server)
seq = 5001, ack = 9001. "I heard your starting number too." Both sides are now ESTABLISHED and can send data

Why three? TCP carries two independent byte streams, one per direction, so each direction needs a "declaration of the starting number (SYN)" and a "confirmation that it was heard (ACK)." That is four roles in total, but the middle two (the server's ACK and the server's SYN) can ride in one packet, so the minimum is three packets. With two, nobody has confirmed that the server's declaration arrived; with four, one packet is wasted.

Practice 2-1 — The three-way handshake, one packet at a time

Confirm what each of the three packets is saying. Work the number questions out on paper.

Q1. What does the second packet of the three-way handshake (the SYN/ACK the server returns) say?

Q2. The client sent a SYN with initial sequence number ISN = 5000. What is the ack number in the SYN/ACK the server returns? (A SYN carries no data but consumes one sequence number.)

Q3. Which is the correct reason a handshake cannot be completed in two steps (ending at SYN → SYN/ACK)?

Teardown has four roles — closing shop one direction at a time

Teardown takes one step more than establishment because ride-sharing is not always possible. The declaration FIN — "I have nothing more to send" — closes only the direction from you to the peer. The peer may still have data left, so after sending a FIN you can keep receiving (half-close). Each direction needs its own FIN and ACK, so the basic form is four packets.

And the side that sent the first FIN lingers in the TIME_WAIT state after everything is done (30 seconds to a few minutes in many implementations). There are two reasons.

Insurance for the final ACK
If the last ACK you returned is lost, the peer retransmits its FIN. If nobody stays behind to answer, the peer cannot close cleanly
Detoxing old segments
Block reuse of the same 4-tuple until this connection's segments still drifting through the network expire. This prevents yesterday's data from contaminating a new connection

There is one more, ill-mannered way to end: RST (reset). It is a unilateral notice — "I know no such connection / I am tearing this down right now" — returned for a SYN to a port with no listener, data addressed to a crashed process, and the like. FIN is closing shop by agreement; RST is summary disposal. Just being able to tell these two apart in a capture changes the whole troubleshooting landscape.

Practice 2-2 — Teardown, TIME_WAIT, and RST

In TCP the ending is deeper than the beginning — and in practice you meet the teardown side far more often.

Q4. Why does TCP teardown need a FIN once in each direction (two in total, each with its own ACK)?

Q5. The side that sends the first FIN does not disappear immediately — it waits for a while in the TIME_WAIT state. What is the purpose of this wait?

Q6. From the client's point of view, what is the difference between sending a SYN to a port nobody is listening on, and a firewall silently dropping the packet?

Key takeaways from this chapter

  • A connection is really a pair of ledgers held by the two ends. Establishment is agreement on the starting numbers (ISNs)
  • The handshake is the minimal form: four roles — SYN + ACK for each direction — ride-shared into three packets. The ack is always 'the next number wanted'
  • Teardown closes one direction at a time with FIN. TIME_WAIT is not a malfunction but insurance for the final ACK and a detox for old segments
  • Immediate RST = it arrived and was refused; silent timeout = no response at all. The first step of triage

In the next chapter, we hand-calculate the stars of the established connection — how sequence numbers and ACK numbers move during data transfer.