# How TCP Three-Way Handshake and Four-Way Wave Work: A Complete Guide to Connection Lifecycle

> Understand the TCP three-way handshake and four-way wave connection lifecycle. Learn how SYN SYN-ACK ACK FIN packets establish and terminate TCP connections efficiently.

- Repository: [Guide/JavaGuide](https://github.com/Snailclimb/JavaGuide)
- Tags: deep-dive
- Published: 2026-02-24

---

**The TCP three-way handshake establishes a full-duplex connection using SYN, SYN-ACK, and ACK packets, while the four-way wave terminates it gracefully with FIN and ACK exchanges to close each direction independently.**

TCP (Transmission Control Protocol) is a connection-oriented, reliable transport-layer protocol that requires explicit connection establishment and teardown. According to the Snailclimb/JavaGuide repository, the mechanics of these control packets are documented in [`docs/cs-basics/network/tcp-connection-and-disconnection.md`](https://github.com/Snailclimb/JavaGuide/blob/main/docs/cs-basics/network/tcp-connection-and-disconnection.md), which provides detailed state diagrams and kernel-level implementation details.

## TCP Three-Way Handshake: Establishing the Connection

Before application data flows, client and server must agree on **Initial Sequence Numbers (ISN)** and verify bidirectional reachability through three synchronized steps:

1. **SYN** – Client → Server: The client sends a `SYN` packet with its initial sequence number (`seq = ISN_C`), entering the **SYN_SENT** state.
2. **SYN+ACK** – Server → Client: The server acknowledges with `ACK` (setting `ack = ISN_C+1`) and synchronizes its own ISN (`seq = ISN_S`), entering **SYN_RCVD**.
3. **ACK** – Client → Server: The client acknowledges the server's ISN (`ack = ISN_S+1`), moving both endpoints to the **ESTABLISHED** state.

This exchange ensures both parties have confirmed each other's ISN and validated that the network path is operational in both directions. The design prevents "ghost" connections from delayed duplicate SYNs by requiring a closed-loop confirmation before transitioning to **ESTABLISHED**.

## TCP Four-Way Wave: Terminating the Connection

Because TCP is **full-duplex**, each transmission direction must close independently, requiring four distinct packets:

1. **FIN** – Initiator → Peer: When one side finishes sending data, it transmits a `FIN` packet (`seq = u`) and enters **FIN_WAIT_1**.
2. **ACK** – Peer → Initiator: The peer acknowledges the FIN (`ack = u+1`) and enters **CLOSE_WAIT**, while the initiator moves to **FIN_WAIT_2**.
3. **FIN** – Peer → Initiator: After transmitting remaining data, the peer sends its own `FIN` packet (`seq = y`) and enters **LAST_ACK**.
4. **ACK** – Initiator → Peer: The initiator acknowledges the final FIN (`ack = y+1`), entering **TIME_WAIT** before eventually reaching **CLOSED** after 2×MSL (Maximum Segment Lifetime).

The **TIME_WAIT** state ensures that stray duplicate FINs or delayed packets from the closed connection are safely discarded, preventing data corruption in subsequent connections that might reuse the same port numbers.

## Kernel Implementation: SYN and Accept Queues

The Snailclimb/JavaGuide source highlights critical kernel data structures that manage the handshake process:

- **Half-connection queue (SYN Queue)**: Stores connections that have completed the first two handshake steps (received client's SYN and sent SYN-ACK) but are awaiting the final ACK. This queue is bounded by `net.ipv4.tcp_max_syn_backlog`.
- **Full-connection queue (Accept Queue)**: Holds connections that have finished the three-way handshake and are waiting for the application to call `accept()`. When this queue fills, the kernel may drop incoming connections or enable **SYN cookies** to prevent SYN flood attacks.

## Practical TCP in Java

While the TCP state machine operates at the OS level, Java developers interact with these mechanisms through standard socket APIs. The following examples demonstrate how the three-way handshake and four-way wave manifest in application code.

### TCP Server Implementation

```java
import java.io.*;
import java.net.*;

public class TcpServer {
    public static void main(String[] args) throws IOException {
        try (ServerSocket server = new ServerSocket(8080)) {
            System.out.println("Server listening on 8080...");
            
            // accept() blocks until the 3-way handshake completes
            Socket client = server.accept();  // State: ESTABLISHED
            
            BufferedWriter out = new BufferedWriter(
                new OutputStreamWriter(client.getOutputStream()));
            out.write("Connection established via 3-way handshake\n");
            out.flush();
            
            // close() initiates the 4-way wave
            client.close();  // State transitions: FIN_WAIT_1 → TIME_WAIT
        }
    }
}

```

### TCP Client Implementation

```java
import java.io.*;
import java.net.*;

public class TcpClient {
    public static void main(String[] args) throws IOException {
        // Constructor performs the 3-way handshake (SYN → SYN-ACK → ACK)
        try (Socket socket = new Socket("localhost", 8080)) {
            BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));
            System.out.println("Server response: " + in.readLine());
            
            // Exiting try-with-resources triggers socket.close()
            // Initiates FIN → ACK → FIN → ACK sequence
        }
    }
}

```

Under the hood, `new Socket()` triggers the operating system to transmit the **SYN** and wait for **SYN-ACK** before returning, while `socket.close()` sends the first **FIN** packet to begin the termination sequence documented in [`docs/cs-basics/network/tcp-connection-and-disconnection.md`](https://github.com/Snailclimb/JavaGuide/blob/main/docs/cs-basics/network/tcp-connection-and-disconnection.md).

## Summary

- The **three-way handshake** (SYN, SYN-ACK, ACK) establishes a full-duplex TCP connection by synchronizing ISNs and verifying bidirectional reachability.
- The **four-way wave** (FIN, ACK, FIN, ACK) terminates connections independently in each direction, allowing residual data transmission while preventing data loss.
- Kernel **SYN queues** and **accept queues** manage connection state during the handshake, with parameters like `tcp_max_syn_backlog` controlling resource limits.
- The **TIME_WAIT** state (2×MSL) ensures reliable connection teardown by absorbing delayed packets from the previous session.

## Frequently Asked Questions

### Why does TCP use a three-way handshake instead of two?

A two-way handshake would only confirm that the server can reach the client, but not vice versa. The third ACK packet verifies that the client received the server's ISN and guarantees bidirectional reachability. This prevents "ghost" connections caused by delayed SYN packets from previous connection attempts that could erroneously establish a connection without both parties actively participating.

### What happens if the final ACK in the four-way wave is lost?

If the final ACK from the initiator is lost, the peer remains in the **LAST_ACK** state and will retransmit its **FIN** packet after a timeout. The initiator, already in **TIME_WAIT**, will receive the duplicate FIN and retransmit the final ACK. The 2×MSL **TIME_WAIT** duration is specifically calculated to allow enough time for this retransmission cycle to complete, ensuring both sides eventually reach the **CLOSED** state.

### How do SYN cookies prevent SYN flood attacks?

When the **half-connection queue (SYN Queue)** fills, the kernel enables SYN cookies by encoding connection state (MSS, timestamp, server IP/port) into the TCP sequence number sent in the **SYN-ACK** rather than storing state in memory. If the client is legitimate and returns the final **ACK**, the server can reconstruct the connection context from the acknowledgment number, allowing connection establishment without consuming queue resources for spoofed SYN requests.

### Can application data be sent during the TCP handshake?

No. The TCP specification requires that the three-way handshake complete and both endpoints reach the **ESTABLISHED** state before transmitting application payload. However, TCP Fast Open (TFO) extensions allow data in the initial SYN packet under specific conditions, though this requires cryptographic cookies and is not the standard behavior described in the Snailclimb/JavaGuide documentation.