How does a TCP connection close? Walk through the four-way close and the states on each side.
Show the answer
Say this first
Each direction closes on its own with a FIN. The side that closes first sends FIN and enters FIN_WAIT_1. The peer ACKs it and enters CLOSE_WAIT; the first side moves to FIN_WAIT_2. When the peer's application closes, the peer sends its own FIN and enters LAST_ACK. The first side ACKs that FIN and sits in TIME_WAIT for two maximum segment lifetimes; the peer goes to CLOSED when that ACK arrives.
Go deeper
The sequence
Active closer (A) Passive closer (B)
ESTABLISHED ESTABLISHED
FIN ->
FIN_WAIT_1 CLOSE_WAIT (B's app is told: end of stream)
<- ACK
FIN_WAIT_2 ... B may keep sending data ...
<- FIN LAST_ACK (B's app called close())
TIME_WAIT
ACK -> CLOSED
(after 2 x MSL) CLOSEDThree segments instead of four
If B's application closes straight away, B sends its FIN in the same segment as the ACK. A capture then
shows [F.], [F.], [.]. That is still the same state machine, just with the ACK and FIN combined.
Half-close
Between FIN_WAIT_2 and LAST_ACK, B can still send data and A must still receive and acknowledge it.
shutdown(SHUT_WR) uses this on purpose: "I have finished sending, now give me the result".
Simultaneous close
If both sides send FIN at the same time, each goes FIN_WAIT_1, then CLOSING when it receives the other's FIN, then TIME_WAIT when its own FIN is acknowledged. Both sides end in TIME_WAIT.
They will ask next
What happens if the final ACK is lost?
B, in LAST_ACK, retransmits its FIN. A is still in TIME_WAIT, so it recognises the FIN and sends the ACK again. That is one of the two reasons TIME_WAIT exists.
How long can a connection stay in FIN_WAIT_2?
The protocol has no limit, because the peer may still be sending. Linux limits sockets the application has already closed (orphans) to
net.ipv4.tcp_fin_timeout, 60 seconds by default, then drops them.Which side ends up in TIME_WAIT in a normal web request?
Whoever sends the first FIN. With HTTP keep-alive the server often closes idle connections first, so TIME_WAIT builds up on the server. A client like curl that exits after one request closes first and holds the TIME_WAIT itself.
Traps
- Mixing up CLOSE_WAIT and TIME_WAIT. CLOSE_WAIT is on the passive side waiting for its own app; TIME_WAIT is on the active side after the close.
- Saying a FIN closes the connection in both directions. It only ends the sender's direction.