Fundamentals 23 min read

Why TCP Can Close Connections in 2, 3, or 4 Handshakes – Hidden Mechanics Explained

This article explores TCP’s connection termination process, detailing the classic four‑way handshake, scenarios that reduce it to three or two handshakes, the concept of self‑connections, simultaneous opens, and practical implications for developers, including code examples and troubleshooting tips.

NiuNiu MaTe
NiuNiu MaTe
NiuNiu MaTe
Why TCP Can Close Connections in 2, 3, or 4 Handshakes – Hidden Mechanics Explained

TCP is a connection‑oriented, reliable, byte‑stream transport protocol. Establishing, using, and releasing a connection correspond to the three‑way handshake, data transfer, and four‑way handshake respectively.

TCP是什么
TCP是什么

TCP Four‑Way Handshake

When data transmission is finished, either side can initiate the four‑way handshake to release the connection. The active side sends a FIN (via close() or shutdown()), indicating it will no longer send data.

The passive side acknowledges with an ACK. If the passive side still has data to send, it may transmit it before sending its own FIN. Finally the active side acknowledges the second FIN with an ACK.

TCP四次挥手
TCP四次挥手

Is a FIN always generated by close() or shutdown()?

Not necessarily. Calling close() or shutdown() on a socket will generate a FIN, but the FIN is also sent when a process exits (whether normally or via kill), regardless of which side initiates the exit.

FIN means "I will not send more data"; therefore shutdown() on the read side does not send a FIN, while shutting down the write side does.

Why might FIN‑WAIT‑2 be abundant on a machine?

FIN‑WAIT‑2 is the state of the active side waiting for the third handshake ( FIN) from the passive side. If the passive side never calls close() to send that FIN, the active side remains in FIN‑WAIT‑2, and the peer will accumulate CLOSE_WAIT sockets.

FIN‑WAIT‑2特别多的原因
FIN‑WAIT‑2特别多的原因

What happens to data received after the active side calls close()?

If the receive buffer still holds data, the kernel may send a RST. If the send buffer still has data, the kernel will wait for it to be transmitted before sending the first FIN. Because TCP is full‑duplex, close() closes both send and receive directions.

Can data be transferred between the second and third handshakes?

Yes. Using shutdown() with SHUT_WR performs a half‑close, allowing the application to continue receiving data while no more data is sent.

int shutdown(int sock, int howto);
howto can be: SHUT_RD – close read side. SHUT_WR – close write side (pending data is still sent). SHUT_RDWR – close both sides (equivalent to close() ).
shutdown触发的TCP四次挥手
shutdown触发的TCP四次挥手

How to detect whether the peer used close() or shutdown()?

The passive side only sees a FIN, regardless of which method the active side used. If the passive side later reads and receives EOF, it knows the active side has closed its write side.

What if the passive side never sends the third handshake?

If the active side used shutdown(SHUT_WR), it stays in FIN‑WAIT‑2 until the peer finally sends FIN. If it used close(), it remains in FIN‑WAIT‑2 for net.ipv4.tcp_fin_timeout (typically 60 s) before moving to CLOSED without entering TIME‑WAIT.

# cat /proc/sys/net/ipv4/tcp_fin_timeout
60
一直不发第三次挥手的情况
一直不发第三次挥手的情况

TCP Three‑Way Handshake Variants

When the passive side has no data to send after the first FIN, the second and third handshakes can be merged, resulting in a three‑handshake termination.

TCP三次挥手
TCP三次挥手

What about delayed ACK?

Delayed ACK allows the receiver to combine acknowledgments, which can further merge the second and third handshakes.

TCP三次挥手延迟确认
TCP三次挥手延迟确认

TCP Two‑Way Handshake (Self‑Connection)

If both ends use the same IP and port, the connection is a TCP self‑connection. The handshake is three‑way, but the termination can be done in just two steps.

TCP两次挥手
TCP两次挥手

Reproducing a self‑connection with code

Binding a client socket to a specific port and then connecting to the same address creates a self‑connection.

# cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
# nc -p 6666 127.0.0.1 6666
# netstat -nt | grep 6666
tcp        0      0 127.0.0.1:6666          127.0.0.1:6666          ESTABLISHED

A C program that performs the same steps is also provided (binding, connect, read/write, close).

Self‑connection troubleshooting

Ensure client and server ports differ (avoid the local port range 32768‑60999 for the server). Alternatively, detect self‑connections in Go’s net library and retry.

TCP Simultaneous Open

Two clients can connect to each other without a listening server, using simultaneous open. This also follows a four‑way handshake.

TCP同时打开
TCP同时打开

Reproducing simultaneous open

while true; do nc -p 2224 127.0.0.1 2223 -v; done
while true; do nc -p 2223 127.0.0.1 2224 -v; done

After retries, both sockets reach ESTABLISHED state.

Summary

TCP termination can involve four, three, or even two handshakes depending on data flow and use of shutdown().

FIN‑WAIT‑2 accumulation usually indicates the peer is not sending its final FIN. close() closes both directions; shutdown() can close only read or write.

Self‑connections use two‑handshake termination; simultaneous opens use four‑handshake establishment without a server.

TCPSocketnetwork fundamentalsConnection terminationHandshake
NiuNiu MaTe
Written by

NiuNiu MaTe

Joined Tencent (nicknamed "Goose Factory") through campus recruitment at a second‑tier university. Career path: Tencent → foreign firm → ByteDance → Tencent. Started as an interviewer at the foreign firm and hopes to help others.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.