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.
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.
seq = 5000. "I want to connect. I start counting from 5000." A SYN carries no data but consumes one numberseq = 9000, ack = 5001. "I heard up to 5001 (= I received your SYN). I start counting from 9000" — confirmation and declaration riding togetherseq = 5001, ack = 9001. "I heard your starting number too." Both sides are now ESTABLISHED and can send dataWhy 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?
SYN/ACK is two roles riding in one packet. The ACK part confirms the client's SYN; the SYN part declares the server's own starting point (the server-side ISN). TCP carries an independent byte stream in each direction, so each direction needs its own SYN and its own ACK. Packing those into three packets gives this shape.
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.)
The ack number is 'the next number I want.' By convention a SYN consumes one sequence number, so a server that received a SYN with seq = 5000 wants 5001 next. Hence ack = 5001. This 'return the next number you want' rule is used in exactly the same form for data transfer in Chapter 3.
Q3. Which is the correct reason a handshake cannot be completed in two steps (ending at SYN → SYN/ACK)?
If it ended after two steps, the server would have to declare the connection 'established' without knowing whether its SYN/ACK ever arrived. It would also instantly create connections for stale, delayed SYNs, holding ghost connections with no peer. The third ACK is the confirmation 'I heard your starting number too.' The SYN + ACK for each of the two directional streams — four roles in total — compressed into three packets by ride-sharing: that is the minimum. Exchanging encryption keys is TLS's job and is not part of TCP.
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.
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)?
A FIN is a one-directional declaration: 'in the direction from me to you, there is nothing more to send.' The peer may still have data left to send, so after sending your FIN you can keep receiving the peer's data (half-close). FIN + ACK for each direction makes four packets in the basic form (sometimes three with ride-sharing) — establishment gets away with three while teardown takes four because of this 'close one direction at a time' design.
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?
TIME_WAIT has two roles. (1) If the final ACK you returned is lost, the peer will retransmit its FIN — so you stay around to answer. (2) You wait until old segments still drifting through the network expire, preventing yesterday's data from slipping into a new connection that reuses the same (source IP, source port, destination IP, destination port). In other words it is not a malfunction but a normal state that keeps the promise to the very end. Chapter 7 covers the 'TIME_WAIT flood' case.
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?
When a SYN reaches a port with no listener, the host's OS immediately returns an RST (reset), and the client fails right away with Connection refused. With a silently dropping firewall, there is no response at all, so the client keeps retransmitting the SYN, waits, and finally times out. 'Immediate refusal = the packet reached the peer host'; 'timeout = no response is coming back' — this distinction stars in the Chapter 7 case exercises.
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.