Topic Overview

TCP Connection Termination (FIN/ACK)

Understand how TCP connections are terminated gracefully using FIN and ACK packets in a four-way handshake.

Beginner8 min read

TCP connection termination uses a four-way handshake to ensure both sides of the connection can send all remaining data before closing. Unlike the three-way handshake for connection establishment, termination requires four packets because TCP is full-duplex—each side must independently close its sending direction.


The Four-Way Handshake

TCP connection termination follows this sequence:

Client                          Server
   |                               |
   | 1. FIN (seq=x)                |
   |------------------------------>|
   |                               |
   | 2. ACK (ack=x+1)               |
   |<------------------------------|
   |                               |
   | 3. FIN (seq=y)                 |
   |<------------------------------|
   |                               |
   | 4. ACK (ack=y+1)               |
   |------------------------------>|
   |                               |

Step-by-Step Breakdown

Step 1: Client sends FIN

  • Client sets FIN flag and includes sequence number x
  • Client enters FIN_WAIT_1 state
  • Client can still receive data but cannot send more data

Step 2: Server sends ACK

  • Server acknowledges the FIN with ACK (ack=x+1)
  • Server enters CLOSE_WAIT state
  • Server can still send data to client (half-close)

Step 3: Server sends FIN

  • Server sends its own FIN with sequence number y
  • Server enters LAST_ACK state
  • Server has finished sending data

Step 4: Client sends ACK

  • Client acknowledges server's FIN with ACK (ack=y+1)
  • Client enters TIME_WAIT state
  • After 2MSL (Maximum Segment Lifetime), client enters CLOSED state

TCP States During Termination

Client States

  • FIN_WAIT_1: Client sent FIN, waiting for ACK
  • FIN_WAIT_2: Client received ACK, waiting for server's FIN
  • TIME_WAIT: Client received FIN and sent ACK, waiting 2MSL
  • CLOSED: Connection fully closed

Server States

  • CLOSE_WAIT: Server received FIN, can still send data
  • LAST_ACK: Server sent FIN, waiting for ACK
  • CLOSED: Connection fully closed

TIME_WAIT State

The TIME_WAIT state lasts for 2MSL (Maximum Segment Lifetime, typically 30-120 seconds). This serves two purposes:

  1. Prevent old duplicate packets: Ensures any delayed packets from the connection are discarded
  2. Ensure final ACK delivery: If the final ACK is lost, the server will retransmit FIN, and client can respond

Why 2MSL?

  • 1MSL for the final ACK to reach server
  • 1MSL for any retransmitted FIN to reach client

Examples

Normal Termination Flow

1import net from 'net';
2
3// Server side
4const server = net.createServer((socket) => {
5 // Receive data
6 socket.on('data', (data) => {
7 console.log(`Received: ${data}`);
8
9 // Send response
10 socket.write('Response data');
11
12 // Close connection (sends FIN)
13 socket.end(); // Sends FIN, enters LAST_ACK

Half-Close Connection

TCP allows one side to close its sending direction while keeping receiving open:

1import net from 'net';
2
3// Server can close sending but keep receiving
4socket.end(); // Close write direction (sends FIN)
5// Can still receive data
6socket.on('data', (data) => {
7 console.log(`Received: ${data}`);
8});
9// Fully close when done
10socket.destroy();

Connection Reset (RST)

If a connection must be terminated immediately (not gracefully):

# Forceful termination with RST
# This happens when:
# 1. Port is closed
# 2. Connection doesn't exist
# 3. Application calls close() on a connection in error state

# RST packet has RST flag set, no ACK required
# Both sides immediately enter CLOSED state

Common Pitfalls

  • Not handling TIME_WAIT: Applications that rapidly create/destroy connections may exhaust ports. Fix: Use connection pooling or SO_REUSEADDR socket option
  • Ignoring half-close: Not properly handling SHUT_WR can cause applications to hang. Fix: Always check for EOF after shutdown
  • RST vs FIN confusion: RST is immediate termination, FIN is graceful. Fix: Use FIN for normal shutdown, RST only for error conditions
  • Not waiting for final ACK: Closing socket immediately may lose data. Fix: Use proper shutdown sequence
  • Port exhaustion: Too many connections in TIME_WAIT state. Fix: Implement connection reuse, adjust TIME_WAIT timeout, or use SO_REUSEADDR

Interview Questions

Beginner

Q: What is the difference between TCP connection establishment and termination? Why does termination need four packets?

A:

Connection Establishment (3-way handshake):

  • SYN → SYN-ACK → ACK
  • Establishes bidirectional communication
  • Both sides agree on initial sequence numbers

Connection Termination (4-way handshake):

  • FIN → ACK → FIN → ACK
  • Each side independently closes its sending direction
  • TCP is full-duplex: each direction must be closed separately

Why four packets?

  • Client closes its sending direction (FIN) → Server ACKs
  • Server closes its sending direction (FIN) → Client ACKs
  • Since each side can independently close, we need two FINs and two ACKs

Intermediate

Q: Explain the TIME_WAIT state. Why does it exist, and what problems can it cause?

A:

Purpose of TIME_WAIT:

  1. Prevent old duplicate packets: Ensures any delayed/duplicate packets from the connection are discarded before a new connection uses the same port pair
  2. Ensure final ACK delivery: If the final ACK is lost, the server will retransmit FIN, and the client in TIME_WAIT can respond

Duration: 2MSL (Maximum Segment Lifetime, typically 60-120 seconds)

Problems:

  • Port exhaustion: High-frequency connection creation/destruction can exhaust available ports
  • Resource usage: Connections in TIME_WAIT consume kernel resources
  • Delayed binding: Cannot immediately reuse the same port pair

Solutions:

# Option 1: SO_REUSEADDR (allows binding to TIME_WAIT port)
socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Option 2: Connection pooling (reuse connections)
# Option 3: Adjust TIME_WAIT timeout (system-level, not recommended)

When TIME_WAIT is on client vs server:

  • Client: Usually in TIME_WAIT (initiates close)
  • Server: Usually in CLOSED (receives close)
  • If server closes first, server enters TIME_WAIT

Senior

Q: Design a high-performance TCP server that handles millions of connections. How do you optimize connection termination and handle TIME_WAIT states?

A:

Design Considerations:

1class HighPerformanceTCPServer {
2 private connectionPool: Map<string, Connection>;
3 private reuseConnections: boolean = true;
4
5 constructor() {
6 // Enable SO_REUSEADDR to reuse TIME_WAIT ports
7 this.socket.setOption(SO_REUSEADDR, true);
8
9 // Connection pooling to minimize connection churn
10 this.connectionPool = new Map();
11 }
12
13 // 1. Connection Reuse
14 async getConnection(clientId: string): Promise<Connection

Optimization Strategies:

  1. Connection Pooling: Reuse connections instead of creating new ones
  2. SO_REUSEADDR: Allow binding to ports in TIME_WAIT state
  3. Connection Draining: Gracefully close connections during shutdown
  4. Load Balancer Health Checks: Use keep-alive to maintain connections
  5. Ephemeral Port Range: Increase system's ephemeral port range
  6. Connection Tracking: Monitor and cleanup TIME_WAIT connections
  7. HTTP Keep-Alive: For HTTP servers, use persistent connections

Monitoring:

# Check TIME_WAIT connections
ss -tan | grep TIME-WAIT | wc -l

# Check connection states
netstat -an | grep TIME_WAIT

# Monitor port usage
ss -s

  • Three-Way Handshake (TCP) - Understanding TCP connection establishment complements learning about connection termination

  • TCP vs UDP - TCP's connection-oriented nature requires graceful termination, unlike connectionless UDP

  • Connection Pooling - Connection pooling helps avoid TIME_WAIT state issues by reusing connections instead of creating new ones

  • HTTP/1 vs HTTP/2 vs HTTP/3 - HTTP connections use TCP, understanding termination helps optimize HTTP connection management

  • OSI Model (7 Layers) - TCP connection termination occurs at Layer 4 (Transport), understanding the OSI model provides context

  • Four-way handshake: TCP termination requires four packets (FIN → ACK → FIN → ACK) because TCP is full-duplex

  • TIME_WAIT state: Lasts 2MSL to prevent old duplicate packets and ensure final ACK delivery

  • Half-close: TCP allows closing send direction while keeping receive open using shutdown(SHUT_WR)

  • Graceful vs forceful: FIN is graceful termination, RST is immediate termination

  • State management: Client typically enters TIME_WAIT, server enters CLOSED (unless server closes first)

  • Port exhaustion: High-frequency connections can exhaust ports due to TIME_WAIT; use connection pooling or SO_REUSEADDR

  • Connection reuse: Reuse connections when possible to minimize TIME_WAIT overhead

  • Best practice: Always close connections gracefully unless error conditions require immediate termination

Key Takeaways

Four-way handshake: TCP termination requires four packets (FIN → ACK → FIN → ACK) because TCP is full-duplex

TIME_WAIT state: Lasts 2MSL to prevent old duplicate packets and ensure final ACK delivery

Half-close: TCP allows closing send direction while keeping receive open using `shutdown(SHUT_WR)`

Graceful vs forceful: FIN is graceful termination, RST is immediate termination

State management: Client typically enters TIME_WAIT, server enters CLOSED (unless server closes first)

Port exhaustion: High-frequency connections can exhaust ports due to TIME_WAIT; use connection pooling or SO_REUSEADDR

Connection reuse: Reuse connections when possible to minimize TIME_WAIT overhead

Best practice: Always close connections gracefully unless error conditions require immediate termination


About the author

InterviewCrafted helps you master system design with patience. We believe in curiosity-led engineering, reflective writing, and designing systems that make future changes feel calm.