Loading learning content...
In human communication, ending a conversation gracefully requires mutual acknowledgment—a simple "goodbye" from one party and a corresponding acknowledgment from the other. In the world of TCP, with its unwavering commitment to reliability, ending a connection is far more ceremonial and deliberate than starting one.
While the TCP three-way handshake establishes connections with elegant efficiency, connection termination demands a four-way handshake—an intentionally asymmetric process that ensures both parties have completed their data transfer, acknowledged all outstanding segments, and can safely release connection resources without losing a single byte.
This asymmetry isn't accidental. It reflects a profound architectural decision: connections are bidirectional, and each direction must be closed independently. This page explores the four-way handshake in exhaustive detail, revealing why TCP's designers chose this approach and how it guarantees the reliability that TCP promises.
By the end of this page, you will understand the complete four-way handshake mechanism, including the purpose of each message, the state transitions involved, the timing considerations, and why TCP requires four segments to terminate what took only three to establish. You'll gain the depth expected of a principal engineer diagnosing connection teardown issues in production systems.
Why is termination different from establishment?
TCP connection establishment uses three segments (SYN, SYN-ACK, ACK) to synchronize both endpoints and establish bidirectional communication. This works efficiently because both sides can piggyback their acknowledgments and synchronization in combined messages.
Connection termination, however, faces a fundamental asymmetry: either endpoint can initiate closure independently, and the other endpoint may still have data to send. This creates the need for a four-segment exchange.
The Bidirectional Nature of TCP
A TCP connection consists of two independent, unidirectional data streams:
Each stream must be closed separately. When the client finishes sending data, it closes its outbound stream, but the server may still be transmitting. The four-way handshake respects this independence.
A critical distinction: closing a TCP connection means closing both streams. The four-way handshake ensures each stream is closed with proper acknowledgment, guaranteeing that all data in transit is delivered before resources are released.
The Four Segments Explained
The four-way handshake consists of:
Each FIN closes one direction of data flow. Each ACK confirms receipt of that closure. Together, they ensure both parties agree that communication has ended.
Let's trace through the four-way handshake step by step, examining the precise sequence of events, the TCP header flags involved, and the state transitions at each endpoint.
Initial State: ESTABLISHED
Both client and server are in the ESTABLISHED state. Data can flow in both directions. Either party can initiate connection termination; for this example, we'll assume the client initiates.
Step 1: Client Sends FIN (Active Close)
The client application calls close() on the socket. The TCP stack responds by:
| Field | Value | Significance |
|---|---|---|
| Source Port | Client's ephemeral port | Identifies sending application |
| Destination Port | Server's service port | Identifies receiving application |
| Sequence Number | X | Last byte sent + 1 (FIN consumes one sequence number) |
| Acknowledgment Number | Y | Next expected byte from server |
| Flags | FIN, ACK | FIN indicates end of data; ACK for previous data |
| Window Size | Client's receive window | Still relevant for incoming data |
Step 2: Server Sends ACK (Passive Close Initiated)
Upon receiving the FIN, the server's TCP stack:
| Field | Value | Significance |
|---|---|---|
| Source Port | Server's service port | Response from server |
| Destination Port | Client's ephemeral port | Directed to client |
| Sequence Number | Y | Server's current sequence number |
| Acknowledgment Number | X + 1 | Acknowledges client's FIN |
| Flags | ACK | Acknowledges the FIN segment |
| Window Size | Server's receive window | Buffer space available |
After sending the ACK, the server enters CLOSE_WAIT. This is a waiting state—the server application must explicitly close its end of the connection. If the application fails to do so (due to bugs or resource leaks), connections can accumulate in CLOSE_WAIT indefinitely, consuming system resources. Monitoring CLOSE_WAIT counts is essential for production health.
Client State Transition: FIN_WAIT_1 → FIN_WAIT_2
When the client receives the server's ACK for its FIN:
Step 3: Server Sends FIN
When the server application finishes sending data and calls close():
| Field | Value | Significance |
|---|---|---|
| Source Port | Server's service port | Server closing its stream |
| Destination Port | Client's ephemeral port | To the initiating client |
| Sequence Number | Y (or Y + n if data was sent) | Server's final sequence number |
| Acknowledgment Number | X + 1 | Maintains acknowledgment |
| Flags | FIN, ACK | Server's finish signal |
| Window Size | Server's receive window | Still included per protocol |
Step 4: Client Sends Final ACK
Upon receiving the server's FIN:
| Field | Value | Significance |
|---|---|---|
| Source Port | Client's ephemeral port | Final response |
| Destination Port | Server's service port | Completing handshake |
| Sequence Number | X + 1 | Client's sequence (unchanged) |
| Acknowledgment Number | Y + 1 (or Y + n + 1) | Acknowledges server's FIN |
| Flags | ACK | Final acknowledgment |
| Window Size | Client's receive window | Standard header field |
Upon receiving the final ACK, the server immediately transitions from LAST_ACK to CLOSED. The server doesn't enter TIME_WAIT—only the active closer (initiator) does. This asymmetry has important implications for server design and port reuse.
Key Observations from the Diagram
Asymmetric Timing: The server can delay its FIN arbitrarily while finishing data transmission. The time between steps 2 and 3 can range from microseconds to minutes.
State Differences: The client goes through FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED, while the server goes through CLOSE_WAIT → LAST_ACK → CLOSED.
Who Waits: Only the active closer (initiator) enters TIME_WAIT. The passive closer goes directly to CLOSED after receiving the final ACK.
Sequence Number Consumption: Each FIN consumes exactly one sequence number, which is why acknowledgments increment by 1 even though no data is transmitted.
A natural question arises: Why can't we combine the server's ACK and FIN into a single segment, reducing termination to three segments?
The answer lies in the nature of application behavior and the separation between acknowledging reception and being ready to close.
Scenario Analysis
Consider a web server handling an HTTP request:
In this scenario, the server's ACK and FIN cannot be combined because there's application work between them. The ACK is sent immediately by the TCP stack, but the FIN is only sent when the application explicitly closes the socket.
If the server application closes immediately upon receiving the client's FIN (no pending data), the server's ACK and FIN CAN be combined into a single segment. This reduces the handshake to three segments but is a special case, not the general rule. TCP must handle the general case where they cannot be combined.
The Separation of Concerns Principle
TCP's design separates several concerns:
This separation ensures that TCP can acknowledge receipt immediately (essential for flow control and retransmission) while allowing applications arbitrary time to complete their work before signaling closure.
Understanding the TCP state machine is essential for debugging connection issues in production. Let's examine each state involved in connection termination in detail.
FIN_WAIT_1: Initiator Awaiting ACK
When the initiating endpoint sends a FIN:
| Attribute | Description |
|---|---|
| Entry Trigger | Application calls close() or shutdown(SHUT_WR) |
| Exit to FIN_WAIT_2 | Receive ACK for our FIN |
| Exit to TIME_WAIT | Receive FIN+ACK simultaneously (rare) |
| Exit to CLOSING | Receive peer's FIN before our ACK |
| Stuck Symptoms | Peer not responding; possible network partition |
FIN_WAIT_2: Initiator Awaiting Peer's FIN
After receiving ACK for its FIN:
FIN_WAIT_2 has no standard timeout. If the peer never sends FIN (application bug, crash, or intentional delay), the connection remains in FIN_WAIT_2 forever, leaking resources. Many operating systems implement non-standard timeouts (e.g., Linux: tcp_fin_timeout, typically 60 seconds) to prevent this.
CLOSE_WAIT: Passive Closer Awaiting Application Close
When a peer's FIN is received:
| Attribute | Description |
|---|---|
| Entry Trigger | Receive FIN from peer while in ESTABLISHED |
| Exit to LAST_ACK | Application calls close(), TCP sends FIN |
| Duration | Unlimited—until application acts |
| Monitoring Importance | High; accumulation indicates application bugs |
| Common Cause of Leaks | Unclosed sockets in server applications |
LAST_ACK: Passive Closer Awaiting Final ACK
After sending its FIN:
TIME_WAIT: The Final Waiting Period
After sending the final ACK:
What happens when both endpoints decide to close at exactly the same time? TCP handles this gracefully through a mechanism called simultaneous close.
The Scenario
The State Transitions
Both endpoints:
The CLOSING state exists only for simultaneous close. It represents a situation where both endpoints have sent FINs but neither has received the ACK for their FIN yet. Upon receiving the ACK, the endpoint transitions to TIME_WAIT, not directly to CLOSED.
Key Observations
Both Enter TIME_WAIT: Unlike normal close where only the active closer enters TIME_WAIT, simultaneous close puts both endpoints in TIME_WAIT.
Four Segments Total: Despite being simultaneous, the handshake still requires four segments—two FINs and two ACKs.
Rare in Practice: Simultaneous close is uncommon in real applications but TCP must handle it correctly.
Protocol Correctness: This scenario demonstrates TCP's state machine completeness—it handles all possible timing scenarios gracefully.
Understanding the four-way handshake has significant implications for application development and system administration.
Application Patterns
When closing sockets, applications have several options:
12345678910111213141516171819
// Option 1: Standard close - initiates four-way handshakeclose(socket_fd); // Option 2: Half-close - close only send directionshutdown(socket_fd, SHUT_WR); // Sends FIN, can still receive// ... receive remaining data from peer ...close(socket_fd); // Option 3: Shutdown both directions then closeshutdown(socket_fd, SHUT_RDWR); // Signals both directionsclose(socket_fd); // Option 4: Abortive close - sends RST instead of FINstruct linger so_linger;so_linger.l_onoff = 1;so_linger.l_linger = 0;setsockopt(socket_fd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));close(socket_fd); // Sends RST, no four-way handshakeMonitoring Connection States
System administrators should regularly monitor TCP states to detect potential issues:
1234567891011121314151617
# View all TCP connections and their statesss -tan # Count connections by statess -s # Watch for CLOSE_WAIT accumulation (indicates app bugs)ss -tan state close-wait | wc -l # Watch for TIME_WAIT accumulation (high connection churn)ss -tan state time-wait | wc -l # Detailed connection info including timersss -tnoe # Using netstat (older but portable)netstat -an | grep -E '(State|ESTABLISHED|CLOSE_WAIT|TIME_WAIT)'Set monitoring alerts for: CLOSE_WAIT > 100 (application socket leak), TIME_WAIT > 10,000 (may need tuning for high-traffic servers), FIN_WAIT_2 > 50 with long durations (peer application issues). These thresholds depend on your specific workload.
The four-way handshake is TCP's solution to the fundamental challenge of reliably closing a bidirectional connection. Let's consolidate the key concepts:
What's Next
Now that we understand the complete four-way handshake flow, the next page examines the FIN and ACK segments in detail—their precise encoding in TCP headers, their role as control signals, and the nuances of their handling by TCP implementations.
You now understand the four-way handshake mechanism in depth—the complete message flow, state transitions, timing considerations, and why TCP requires this deliberate process for reliable connection termination. Next, we'll examine the FIN and ACK control segments in detail.