Opening a connection: The three-way handshake
It's exactly like the ritual that opens a phone call. "Hello, can you hear me?" "Yes, and can you hear me?" "Yes." Three exchanges, not two, because the call needs to be confirmed working in both directions before either side starts talking business. TCP needs the same thing: it's full-duplex, so each side has to pick its own starting sequence number and have the other side confirm it received that number.
| Step | Direction | Flags | Establishes |
|---|---|---|---|
| 1 | Client → Server | SYN | The client's initial sequence number (ISN) |
| 2 | Server → Client | SYN, ACK | The server's own ISN, and confirmation of the client's |
| 3 | Client → Server | ACK | Confirmation of the server's ISN; data can now flow either way |
Two segments aren't enough: if the client only sent SYN and the server replied SYN ACK, the client would know the server's number, but the server would have no confirmation the client actually received it. A fourth segment isn't needed either, since the third segment's ACK can piggyback directly on top of it without adding a separate step. Three is the minimum that confirms both directions.
Closing a connection: Four-way termination
Because TCP is full-duplex, either side can still have data left to send even after the other side is done. Closing a connection means each direction has to be shut down on its own, which is normally four segments instead of two. The side that closes first sends FIN and enters FIN_WAIT; the other side, now in CLOSE_WAIT, can keep sending data on its still-open half of the connection until it's ready to send its own FIN.
| Step | Direction | Flags | What happens |
|---|---|---|---|
| 1 | Initiator → Peer | FIN | Initiator has no more data to send, enters FIN_WAIT |
| 2 | Peer → Initiator | ACK | Peer acknowledges; connection is half-closed, peer enters CLOSE_WAIT |
| 3 | Peer → Initiator | FIN | Peer is also done sending, once it's ready |
| 4 | Initiator → Peer | ACK | Initiator acknowledges, enters TIME_WAIT before fully closing |
After sending the final ACK, the initiator doesn't close immediately. It waits in TIME_WAIT for a period (typically twice the maximum segment lifetime) in case that final ACK got lost and the peer retransmits its FIN. Without this wait, a lost ACK could leave the peer stuck resending a FIN that never gets acknowledged. It's also why a server restarted quickly can sometimes fail to rebind a port that's still finishing TIME_WAIT from a previous connection.
Not every connection ends this gracefully. A RST (reset) tears a connection down immediately, with no half-closed state and no TIME_WAIT: it's what happens when a segment arrives for a port nothing is listening on, or when an application aborts a connection after an error instead of finishing its data cleanly.