3.1 TCP Connection Establishment, Termination, and TIME_WAIT
A socket enables a program to ring the doorbell of another tower, but before the first byte is sent, both ends must confirm that they're still present and exchange their initial sequence numbers. When the connection ends, one direction may close before the other. In the mirror of the communication path, we see states like SYN-SENT, CLOSE-WAIT, and TIME-WAIT, a simple "four-way handshake" explanation isn't enough to account for why they linger there.
The intuitive idea of "a single connection pipe" remains useful, but it's not precise enough. TCP maintains separate sequence number spaces, receive and send windows, retransmission timers, acknowledged ranges, and closure progress at each endpoint. The three-way handshake synchronizes initial sequence numbers and connection states between both sides, while a FIN packet only closes one direction of transmission.
This lesson walks through the timeline of a connection, from the initial handshake to the final teardown. The next lesson opens a letter that's already in transit and explores how sequence numbers, ACKs, and flow control reconstruct a reliable, ordered byte stream from fragmented and potentially out-of-order IP packets.
1. What Identifies a Connection
A common TCP flow is typically identified by a four-tuple:
source IP, source port, destination IP, destination portThe protocol number (e.g., TCP) often completes the five-tuple used in network devices. Servers can allow many connections to share the same local port, since each connection has a unique remote address and port.
NAT, proxies, and load balancers may rewrite or terminate flows, so the four-tuple seen by clients, edge proxies, and backend services might differ. Application-level request IDs are more effective than socket four-tuples for tracing business requests across proxies.
2. Three-Way Handshake Exchanges Two Sequence Number Spaces
Client initiates the connection:
Client Server
| |
| SYN, seq = x |
|---------------------------------------->|
| |
| SYN + ACK, seq = y, ack = x + 1 |
|<----------------------------------------|
| |
| ACK, ack = y + 1 |
|---------------------------------------->|
| ESTABLISHED |Each direction independently selects an initial sequence number (ISN). The SYN packet consumes one sequence number, so the acknowledgment value is ISN + 1.
The third ACK informs the server that the client has received the server’s SYN and ISN. This handshake confirms bidirectional path integrity and the mutual sequence number state, reducing it to “the third packet prevents historical connection issues” would overlook the actual protocol state establishment.
The final ACK may carry application data. TCP Fast Open allows application data to be sent earlier under specific conditions, but introduces replay and middlebox compatibility boundaries. Applications cannot assume that the first data packet will be processed only once.
3. When do connect and accept return?
Blocking connect typically waits until the handshake succeeds or fails; nonblocking connect usually returns immediately with an in-progress state, and the application must later check for writable or error readiness to determine the final outcome of SO_ERROR.
On the server side:
- The listening endpoint receives a SYN packet;
- The kernel creates or encodes a half-open connection state and responds with a SYN-ACK;
- Upon receiving a valid ACK, the connection moves into a queue ready for
accept; - The application calls
acceptto obtain the connected socket.
The implementation may separately manage half-completed handshakes and fully completed connections awaiting accept, with the backlog constrained by kernel configuration. Receiving a SYN-ACK does not mean the application has already accepted the connection; seeing a completed handshake does not imply that the business thread has begun reading.
4. How Handshake Failures Appear Within a Package
SYN retransmission followed by timeout. Common when requests or responses are dropped, routing black holes occur, or firewalls silently drop packets. A client-side timeout alone cannot determine whether the packet was lost in the outbound or inbound direction.
Immediate RST. The target host is reached, but no listener is bound to the endpoint, or an intermediate device actively rejects the connection. Applications typically see a "connection refused" error.
SYN-ACK received, but final ACK lost. The server retransmits the SYN-ACK; the client then re-acknowledges. The protocol relies on retransmission to tolerate such packet loss.
Incorrect address selection. DNS returns multiple addresses, and the first candidate is unreachable while the application fails to promptly try alternative address families. Connection strategies should be aligned with Happy Eyeballs and a global deadline.
Packet capture must be performed on both ends: a client's SYN does not guarantee that the server has received it, and a server's SYN-ACK does not confirm that the client or its kernel has accepted it.
5. Connection State Is Not a Simple Line
Common states:
| State | Meaning |
|---|---|
LISTEN | Waiting for an incoming connection |
SYN-SENT | Actively opened, waiting for SYN/ACK |
SYN-RECEIVED | Received SYN, sent SYN-ACK |
ESTABLISHED | Handshake complete; data transmission is allowed |
FIN-WAIT-1/2 | Local end initiated closure, waiting for peer confirmation or termination |
CLOSE-WAIT | Received peer's FIN, local application has not yet closed the send direction |
LAST-ACK | Passive close side has sent FIN, waiting for final ACK |
CLOSING | Both sides have exchanged FINs, closing state |
TIME-WAIT | Active close path retained; absorbs old segments and handles final retransmissions |
State names belong to the TCP endpoint, not to the entire application. A single process can maintain thousands of connections in different states simultaneously.
6. FIN Closing Only One Direction
As the conversation between the two towers nears its end, the side that speaks first can close its own send path while still continuing to receive replies from the other side. This behavior corresponds to a half-close, not a fourth state invented by TCP.
When a client sends a FIN, it signals that "no more bytes will be sent in this direction," but it does not mean the client refuses to receive:
Client Server
| FIN ----------------------->| client send direction ends
|<----------------------- ACK |
|<----------- response data |
|<----------------------- FIN | server send direction ends
| ACK ----------------------->|This is a half-close. The protocol allows a client to send its request and then shutdown(SHUT_WR), continuing to read the server's response.
The common notion of a "four-way handshake" is just a typical timeline, it doesn't require four separate packets. ACKs can be combined with data or FINs, and both sides may close simultaneously.
A FIN occupies a sequence number, and the receiving end only signals EOF to the application after all prior bytes have arrived in order.
7. CLOSE_WAIT is typically caused by issues in the application's lifecycle
Entering CLOSE-WAIT means:
-The remote end has terminated transmission;
- The kernel on this end has already provided EOF to the application;
- The local application has not yet closed its direction.
A small number of brief CLOSE_WAIT states is normal. A growing number typically indicates:
- Forgot to close after reaching EOF;
- The socket is not released on the exception/cancel path;
- Worker stuck at downstream operation, connect to clearing queue;
- fd is still referenced by another object or thread;
- The TLS/protocol state machine is waiting for an application event that will never come.
Adjusting TCP timeout can't replace an application's need to call close. You must trace back to the file descriptor ownership and call stack.
8. TIME_WAIT is the Watchful Stand After Disconnection
The door is closed, and the final message might still be en route, old letters could arrive late. The side that initiates the closure cannot immediately forget this conversation; otherwise, a retransmitted FIN would have no recipient, and a new connection with the same four-tuple might accidentally receive a stale segment.
For this reason, the side that sends the final ACK typically enters TIME_WAIT and retains a protocol state for approximately 2×MSL. Its primary purposes are:
- If the final ACK is lost, the remote endpoint will retransmit the FIN, and the local side can still respond with an ACK;
- Allow stale duplicate segments from the same four-tuple to expire in the network, preventing them from corrupting a later connection that reuses the same endpoint.
The exact duration is an implementation detail and should not be treated as a permanent TCP constant. TIME_WAIT does not mean the application process still holds a connected file descriptor, nor does it equate to "connection leakage."
A high volume of short-lived connections can generate a large number of TIME_WAIT states, which (combined with ephemeral port exhaustion, NAT table limits, or load-balancer state capacity) can become a system bottleneck. Prioritize connection reuse, reasonable source address and port allocation, and well-designed proxy architectures. Avoid prematurely shortening the TIME_WAIT period using dangerous sysctl settings before you fully understand the protocol's protective requirements.
9. RST is an abrupt termination
A RST causes the remote endpoint to immediately abandon the connection, potentially resulting in loss of unprocessed data and application-level boundaries. Common causes include:
- Connecting to a port without a listener;
- The endpoint does not exist or is in an inconsistent state;
- The application performs an explicit
SO_LINGER-configured abortive close; - The process terminates while still holding unprocessed incoming data, and certain stack implementations choose to reset the connection;
- A middlebox generates or fakes a reset.
A successful return from close() after a send() call does not guarantee that the remote application has processed the message. Reliable application-level protocols require either a response or explicit acknowledgment from the remote side.
SO_LINGER has cross-platform semantics and is prone to causing blocking closes or RSTs; it should not be used as a general technique for rapidly releasing ports.
10. Keepalive and Application Heartbeats
TCP keepalive is a kernel-level mechanism for probing idle connections. Whether it's enabled, how long it waits before initiating a probe, and the retry interval and count are determined by system and socket configurations. It's effective at reclaiming long-idle, silent peers, but its time scale may not align with the actual business requirements for detecting failures.
Application-level heartbeats can carry protocol state and leverage business-defined deadlines, but they introduce additional traffic and risk of false positives. If the event loop stalls, the network experiences brief fluctuations, or garbage collection pauses, a heartbeat timeout does not necessarily indicate that a node has permanently failed.
Neither keepalive nor application heartbeats can replace request-level timeouts. The fact that a connection is "still alive" does not mean that a specific request will complete within its expected time.
11. SYN flood and stateful resources
An attacker can send a large number of SYN packets without completing the handshake, consuming half-open connection state and response bandwidth. Mitigation strategies may include:
- SYN cookies or similar stateless encoding mechanisms;
- Reasonable queue limits and rate limiting;
- Edge scrubbing or load balancing;
- Source validation and network-side policies;
- Monitoring the ratio of SYN, SYN-ACK, completed handshakes, and accepted connections.
SYN cookies trade connection state management for limited connection options or constraints. The specific implementation evolves with the system. Do not treat "enabling cookies" as the complete solution for capacity planning or DDoS protection.
12. Align the Observational Lens with the Kernel
In the story, the observational lens first lands on ss and packet capture. The former reads local socket state, while the latter records segments passing through a specific capture point. Each provides only half the picture, both must be time-aligned to form a complete view.
Linux can begin by inspecting local socket state:
ss -lnt
ss -ntp
ss -sFields and permissions vary by kernel version. The key is aligning the following elements with the packet capture timeline:
- local and peer endpoints
- TCP state
- associated process and file descriptor
- queue state
- timers and retransmission activity
ss only reveals the local kernel state. After proxying, front-end and back-end connections become two distinct TCP flows and must be observed separately.
13. Common Misconceptions
"Three-way handshake verifies a username." It establishes TCP sequence numbers and connection state, not application-level identity authentication.
"Receiving an ACK means the application has processed the data." A TCP ACK indicates receipt of a byte range by the protocol stack, not that the application has acknowledged or processed the data.
"Close simultaneously shuts down bidirectional transmission." A standard close ultimately releases the local reference; at the protocol layer, the two directions can independently send FIN packets.
"TIME_WAIT occurs only on the server." Which side initiates the active close is more critical, architecture and protocol design can shift where this state occurs.
"As long as keepalive is enabled, there are no zombie connections." Keepalive parameters, network conditions, and application deadlines all influence when detection occurs.
14. Summary
The TCP lifecycle is a bidirectional state machine:
- It exchanges and confirms two initial sequence numbers through a three-way handshake;
- The states of listen, half-open connection, waiting for accept, and application processing represent distinct phases;
- A FIN only terminates one direction of transmission; the four packet sequence is not a fixed formula;
- CLOSE_WAIT often indicates that the local application has not completed cleanup;
- TIME_WAIT protects against retransmission of the final ACK and isolates old segments;
- RST signifies an abnormal termination and does not guarantee that application messages are fully delivered;
- keepalive, heartbeat, and request deadline mechanisms address problems at different layers of the protocol stack.
The gate of the Beacon Tower has been knocked, discussed, and withdrawn. The next section, 3.2 Sequence Numbers, ACKs, and Flow Control, moves forward with a letter that arrived in advance, examining how an ESTABLISHED connection reconstructs lost or out-of-order IP packets into a reliable byte stream.