TCP CLOSE_WAIT and FIN_WAIT PCAP Analysis: Finding Connection Leaks, Half-Closes, and Shutdown Bugs
How to analyze TCP CLOSE_WAIT, FIN_WAIT, TIME_WAIT, half-close behavior, connection leaks, missing close calls, FIN packets, RST packets, and shutdown timing in packet captures.
A server accumulating CLOSE_WAIT, a peer stuck in FIN_WAIT, or a connection pool that exhausts file descriptors needs packet-level shutdown chronology. Determine who sent the first FIN, whether it was acknowledged, whether the other half ever closed, and whether a reset or retransmission interrupted the four-way teardown. A PCAP can prove TCP half-close behavior and packets on the wire; only host socket state and process evidence can show why an application left a received FIN in CLOSE_WAIT.
PCAP Surgery is useful because shutdown bugs are timing and direction problems. You need to know who sent FIN, who ACKed it, who failed to close, and whether a reset or timeout followed.
Normal TCP close
A graceful TCP close often uses FIN in both directions:
Peer A -> Peer B: FIN
Peer B -> Peer A: ACK
Peer B -> Peer A: FIN
Peer A -> Peer B: ACK
This four-step close can vary, but direction matters.
CLOSE_WAIT
CLOSE_WAIT means the local application received a FIN from the peer and acknowledged it, but the local application has not closed its side yet.
If many sockets remain in CLOSE_WAIT, the peer already initiated close. The local application likely did not close the socket.
Packet evidence:
- Peer sends FIN.
- Local host ACKs FIN.
- Local host does not send FIN for a long time.
That is often an application resource leak or shutdown-path bug.
FIN_WAIT
FIN_WAIT states occur on the side that initiated close. Long FIN_WAIT may indicate the peer did not acknowledge or did not send its own FIN as expected.
Packet evidence helps identify whether packets were lost, the peer is slow, or application shutdown is incomplete.
RST instead of FIN
Some applications abort with RST instead of graceful FIN. A reset can be normal for error paths, but it may also indicate crashes, unread data on close, load balancer behavior, or timeout policy.
Do not treat FIN and RST as the same. They have different meanings.
Checklist
Use this workflow:
- Identify the TCP conversation.
- Find the first FIN or RST.
- Identify sender.
- Check whether FIN was ACKed.
- Check whether the other side sent its own FIN.
- Measure delay between FIN and final ACK.
- Correlate with socket states on hosts.
- Look for repeated CLOSE_WAIT patterns.
- Preserve shutdown packets when trimming.
- Combine pcap with application close-path logs.
Final diagnosis
TCP CLOSE_WAIT and FIN_WAIT issues are shutdown-state problems. Packet captures can show who initiated close, who acknowledged, who failed to finish, and whether the connection ended gracefully or by reset.
PCAP Surgery helps preserve the exact close sequence so connection leaks and half-close bugs can be diagnosed from packet evidence instead of socket-state snapshots alone.
Build a four-way close timeline, not a state-name report
Socket-state labels come from a host. The PCAP explains the wire sequence at its capture point. Start with the first FIN or RST, then record direction, sequence/acknowledgement relationship, retransmissions, final acknowledgement, and elapsed gaps. This makes a connection leak report reviewable even when the capture does not include the process that owns the socket.
| Wire observation | Careful interpretation | Next evidence |
|---|---|---|
| Peer FIN is ACKed; local side never sends FIN | The local host had a half-close and did not finish within the observed window | Host socket/process close-path logs |
| Local FIN is retransmitted | Peer acknowledgement was not observed at this capture point | Path loss, capture placement, peer health |
| Both FINs and final ACK appear | Graceful close completed on the wire | Compare any lingering host state to capture time |
| RST follows unread data or an error path | Abortive close may have occurred | Application/load-balancer policy and timing |
| New SYN reuses a 4-tuple after a close | New connection must not be merged into old timeline | ISN/time separation and host logs |
Do not state that an application “leaked” a connection solely from a PCAP. The defensible statement is that the capture observed a FIN from one side, an ACK from the other, and no later FIN from that other side during the retained window. The owner of the host can then verify the process state.
Separate FIN_WAIT, CLOSE_WAIT, TIME_WAIT, and reset paths
| State commonly reported by a host | Wire evidence that supports investigation | Common wrong shortcut |
|---|---|---|
| CLOSE_WAIT | Peer FIN followed by local ACK; local FIN delayed or absent | Blaming the peer that already closed |
| FIN_WAIT | Local FIN sent; peer ACK/FIN behaviour is incomplete or delayed | Assuming packet loss without checking ACKs/retransmits |
| TIME_WAIT | Full close has occurred and local stack retains tuple temporarily | Treating a normal cleanup guard as a leak |
| RESET/abort | RST replaces or interrupts graceful close | Calling every reset a network failure |
The TCP RST connection reset guide covers reset-specific evidence. Use the TCP retransmission and duplicate ACK guide only when sequence/ack behaviour actually suggests missing delivery; a silent application close is a different branch.
A safe teardown QA workflow
- Preserve a bounded window before the first FIN/RST and after the last observed packet.
- Group packets by the exact 4-tuple and direction; avoid merging NAT-reused flows.
- Mark first FIN, corresponding ACK, second FIN, final ACK, resets, retransmits, and long gaps.
- Align the capture clock with host metrics only when clock offset is documented.
- Repeat one controlled close path if authorized: normal client exit, server drain, or load-balancer timeout.
- Export a minimal evidence subset with the original capture retained separately.
| QA case | Acceptance evidence |
|---|---|
| Graceful client close | Four-way close sequence and elapsed delays are retained |
| Suspected CLOSE_WAIT | Peer FIN/local ACK plus missing/delayed local FIN is explicit |
| Suspected FIN_WAIT | Initiator FIN, ACK/retry, and peer finish boundary are explicit |
| Reset path | RST direction/sequence and preceding condition are preserved |
| Long-lived pool | Repeated patterns are grouped without hiding distinct connections |
Questions about CLOSE_WAIT and FIN_WAIT
Does CLOSE_WAIT prove a remote server is at fault?
No. It usually means the remote peer initiated closure and the local application had not closed its side. Packet evidence should be combined with the local application's socket ownership and shutdown logs.
Does FIN_WAIT always mean packet loss?
No. It can involve a slow peer, an application that has not closed, an idle-policy boundary, a reset, or unobserved traffic at another point. Check acknowledgements and retransmissions before assigning cause.
What can PCAP Surgery prove?
It can preserve the observed TCP shutdown chronology and make a minimal, time-faithful evidence subset. It cannot reveal a host's hidden socket state, process ownership, or load-balancer configuration without corroborating host data.
The useful conclusion names the side and packet boundary: who first closed, whether it was acknowledged, whether the peer finished, and what remains unproven beyond the capture point.
Evidence handoff for a shutdown incident
Include the 4-tuple, capture point, first FIN/RST time and direction, each acknowledgement/retransmission, final observed packet, elapsed gap, connection count trend, and the matching host/process log window. State whether the capture is client-side, server-side, load-balancer-side, or a tap between them. If one side is not visible, say so. This lets an application owner test the close path rather than treating a socket-state screenshot as a network conclusion.
For a pool-exhaustion case, group repeated flows by the same shutdown pattern and retain several representative conversations, not only the worst one. A repeated “peer FIN → local ACK → no local FIN” pattern is a focused lead; a one-off sequence may be an intentional long-lived half-close. Preserve both the count and the exceptions.
Before changing timeout values, test a controlled graceful-close path and an authorized abnormal-close path independently. A longer idle timeout can mask a close-path bug while retaining resources longer. The preferred fix follows the observed ownership boundary: application close lifecycle, peer behaviour, load-balancer policy, or an unproven path issue.
Keep evidence windows small enough to review but long enough to include the final retry/expiry policy. The first FIN alone is rarely enough; the meaningful decision point is whether its acknowledgement, peer finish, or reset was observed before the retention window ended.
That exact boundary should appear in every final incident summary and regression test.
A compact teardown regression record
For each controlled test, record the initiator, expected close policy, capture point, first FIN/RST time, final observed packet, and whether the peer's FIN was seen. Run the same test through the normal client exit, service drain, and timeout paths rather than assuming one teardown covers them all.
| Regression result | Safe conclusion |
|---|---|
| Four-way close repeats within expected window | Wire-level graceful shutdown is observed |
| Peer FIN is acknowledged but local FIN is absent | Local close-path investigation remains required |
| RST replaces close only on an error path | Abortive behaviour is scoped to that path |
This record makes a later packet capture comparable without pretending host state was visible.
Teardown sequence matrix
Summarize each representative flow as a direction-aware sequence. Socket-state names are local host interpretations; packets give the portable chronology. Do not label a remote host “in CLOSE_WAIT” unless host evidence from that machine confirms it.
| Observed packet sequence | Wire-level conclusion | Evidence still required |
|---|---|---|
| A FIN → B ACK → B FIN → A ACK | Graceful bidirectional close observed | Host reason for initiating close |
| A FIN → B ACK → no B FIN | B acknowledged A’s close; opposite half not observed | B process ownership and close path |
| A FIN retransmitted, no ACK | FIN or ACK path not observed | Capture placement, loss, peer state |
| Data followed by RST | Abortive termination observed | Application, proxy, or policy trigger |
| FIN/ACK complete, repeated new connects | Wire close works but churn exists | Pool/reuse policy and client behavior |
Measure from the first FIN to its ACK, from that ACK to the peer FIN, and from the peer FIN to the final ACK. Preserve retransmissions and the observation period after the last relevant packet. A capture ending immediately after the first FIN cannot prove that the peer never completed shutdown.
Which side should investigate CLOSE_WAIT?
The host that received and acknowledged the peer’s FIN but has not closed its own socket is the application-side lead. The PCAP identifies that direction; ss, netstat, process ownership, thread dumps, and application logs identify the local resource and code path.
Can a load balancer change the diagnosis?
Yes. It terminates one TCP connection and creates another, so client-side and server-side teardown are separate conversations. Capture or correlate both legs and never merge their sequence numbers or FIN directions into one flow.
How much context belongs in the evidence subset?
Include the handshake if available, the final request/response, all FIN/RST packets and retransmissions, and enough post-close time to cover the expected timeout. Preserve several normal and failed examples. Follow capture scope guidance and prepare the derivative with the evidence workflow.
A strong final statement is bounded: “At the server-side capture point, the client sent FIN, the server acknowledged within 1 ms, and no server FIN or RST was observed during the following 60 seconds.” It is stronger than “the network left sockets in CLOSE_WAIT,” which claims host state the capture cannot see.
For regression testing, repeat the same close paths under normal load and controlled resource pressure. Compare not only elapsed close time but the packet sequence and host socket count. A packet fix without falling socket counts may leave an application leak; a host improvement without the expected FIN sequence may rely on timeout cleanup rather than a correct close lifecycle.
<!-- multilingual-related-reading:start -->Related guides
Continue with the same-language pages below. They cover adjacent stages without changing the canonical owner of this topic:
<!-- multilingual-related-reading:end -->