Connection tear-down

The way a TCP connection transitions from an established connection to a closed one is nuanced. Let's consider this in more detail.

TCP connections are full-duplex. This means that the data being sent is independent of the data being received. Data is sent and received simultaneously. This also implies that the connection must be closed by both sides before it is truly disconnected.

To close a TCP connection, each side sends a Finish (FIN) message and receives an ACK message from their peer.

The exact tear-down process, from the perspective of each peer, depends on whether it sent a FIN first, or received a FIN first. There are three basic connection tear-down cases. They are as follows:

  1. You initiate the tear-down by sending the first FIN message
  2. You receive a FIN message from your connected peer
  3. You and your peer send FIN messages simultaneously

In case 3, where both sides send a FIN message simultaneously, each side thinks that it is in case 1. That is, each side thinks that it has sent the first FIN message, and each side tears down its socket as in case 1. In practice, this is pretty rare, but certainly possible.

When a TCP socket is open for full-duplex communication, it is said to be in the ESTABLISHED state. The closing initiator sends a FIN message to its peer. The peer replies with an ACK. At this point, the connection is only half closed. The initiator can no longer send data, but it can still receive data. The peer has the option to continue to send more data to the closing initiator. When the peer is ready to finish closing the connection, it sends its own FIN message. The initiator then responds with the final ACK message, and the connection is fully closed.

The TCP connection state transitions on the initiator are ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, TIME-WAIT, and CLOSED. The TCP connection state transitions on the receiving peer are ESTABLISHED, CLOSE-WAIT, LAST-ACK, and CLOSED.

The following diagram illustrations the normal TCP four-way closing handshake:

It is sometimes possible for the Peer to combine its ACK Message and FIN Message into one message. In that case, the connection can be torn down with only three messages, instead of four.

In the case where both sides initiate the tear-down simultaneously, both sides follow the state transition of the Initiator. The messages sent and received are the same.

Networks are inherently unreliable, so there is a chance that the final ACK Message sent by the Initiator will be lost. In this case, the Peer, having not received an ACK Message, resends its FIN Message. If the Initiator had completely CLOSED its socket after sending the final ACK Message, then it would be impossible to reply to this resent FIN Message. For this reason, the Initiator enters a TIME-WAIT state after sending the last ACK Message. During this TIME-WAIT state, it responds to any retransmitted FIN Message from the Peer with an ACK Message. After a delay, the Initiator leaves the TIME-WAIT state and fully closes its socket.

The TIME-WAIT delay is usually on the order of one minute, but it could be configured for much longer.

In this book, we've used only the close() function (closesocket() on Windows) to disconnect a socket. This function, although simple to use, has the disadvantage of always fully closing a socket. That is, no data can be sent or received on a socket called with close(). The TCP teardown handshake does allow for data to be received after a FIN message has been sent. Let's next consider how to do this programmatically.