In this series, you will learn about crucial (but easily overlooked) details of the most important (and complicated) protocol in computer networking, aka TCP. For your reference, below is a list of the articles in this series:
- Part 1: connection establishment
- Part 2: connection termination (this article)
- Part 3: congestion control
We continue from part 1 of the series and discuss the termination of TCP connection in this post.
TCP Connection Termination
Again, let’s bring up the tcp transition state.
Courtesy to IBM.com 1
The normal TCP termination involves 2 pairs of 2-ways communication, i.e. total of 4 segments transferred:
- The side that initiates the termination (usually the client) is called active closer, it sends FIN+ACK packet to server, the ACK is just to acknowledge the last data sent:
FIN: 1, ACK: 1 Acknowledgment Number: current_ack_client Sequence Number: current_seq_client
- The server or passive closer receives the client’s message and replies with ACK packet, it notifies from application layer to perform its own termination operation:
FIN: 0, ACK: 1 Acknowledgment Number: current_seq_client + 1 Sequence Number: current_ack_client
- If the server’s application layer decides to terminate the connection. The server will sends FIN+ACK packet. Notice that the numbers used here is the same as step 2:
FIN: 1, ACK: 1 Acknowledgment Number: current_seq_client + 1 Sequence Number: current_ack_client
- The client finalize the process with a ACK packet:
FIN: 0, ACK: 1 Acknowledgment Number: current_ack_client + 1 Sequence Number: current_seq_client
TCP connection termination only starts when the application layer from one host indicates its desire to close with close() call.If the application layer did not signal the termination, TCP connection will never be closed if there is nothing wrong with the connection. But what happens if there is something wrong with the connection, i.e. a host in connection cannot send data (using send() call). Let’s look at a few examples later but we first need to understand the RST flag.
A reset segment is simply the one with no payload and with the RST bit set in the TCP header flags, the ACK bit field must also be set and the ACK Number field must be within the valid window to prevent the forging of reset segment and disrupt the connection. A reset segment is sent by a host when it receives unexpected packets, i.e.
- The connection is aborted, i.e. the receiver process is killed
- The SYN packet arrives when there is no process listening on the destination port. Reset segment is sent with sequence number of 0 (the acknowledgment number is still the client’s seq number + 1)
Reset segment might have payload to Unlike other types of packet, reset segment does not require ACK and expect the immediately close (or reset) of the connection. This is different from a FIN, which just says that the sender of FIN will not send any new data; but it has not terminated very soon and can still accept data.
Let’s now look at a few examples when the client cannot send the data after connection has been established:
If the server process is killed, all open descriptors are closed and a FIN is sent to the client, and the client responds with an ACK. At this moments, server’s process has been killed but client does not know. If client now sends data to server, the arriving packet is considered unexpected, and as we discussed above, the server host will send reset segment to the client.
If the physical server host is disconnected from the network, and when the client sends data, it will not receive the ACK, and will retry until timeout and eventually gives up. Note that if client does not active sending data and without TCP keepalive feature, client is not aware of server situation)
If the physical server host is disconnected, rebooted and then reconnected back to the network (we assume that no data is sent when the server is disconnected). When the server is up again, it does not have all information about connections that existed before the crash. Therefore, the server also respond the data from client with an RST.
If the physical server host is shutdown by the operator, the networking process is gracefully terminated and the sequence is similar to scenario 1 above.
The final state of the active closer (the one that initiated the termination and usually the client) is called TIME_WAIT. The active closer enters this state after receiving FIN and ACKs it (it had also sent its own FIN and received ACK earlier), it now waits for timer to expire before move to CLOSED state.
TIME_WAIT value is double the maximum segment life(MSL) - the maximum amount of time any segment can exist in the network before being discarded. This time limit is bounded, because TCP segments are transmitted as IP datagrams, and the IP datagram has the TTL field that limits its effective lifetime. TCP defines this value to be 120 s, but it allows implementation of lower value, i.e. it is 60 in Linux
$ cat /proc/sys/net/ipv4/tcp_fin_timeout 60
The interesting question is why client needs to wait? There are a few reason for this:
- TIME_WAIT ensures ACK is received by the passive closer. If it is not, the passive closer might re-send the FIN packet, so active closer needs to hang on a while in case it needs to re-send the ACK to this new FIN.
- The more unlikely reason is that TIME-WAIT provides a buffering period between two consecutive connections so that the delayed segments will not be confused between different instantiation on the same connection. However, this is only possible with 2 conditions 1) The OS picks the same client’s port for the second connection and as we know, the chance is not very high since OS actually randomizes this in the case of the client; and 2) The sequence number of the delayed segments must overlaps of those from new connection.
However, TIME-WAIT introduced a few implications:
- If the client is active closer, you cannot immediately re-use the same local port number, but again this is not a big deal since OS has a wide range of ephemeral ports to assign to client, unless of course clients make huge number of connections and run out of ports.
- If the server is active closer, i.e. you want to restart your server, but since server usually runs on a well-known port, this will cause “Address already in use” binding error.
Having said that, some specifications do allow the reuse of port number before TIME-WAIT is over, so long as segments in different instantiations are not confused each other, i.e.
- when a new instantiation uses an ISN that exceeds the highest sequence number used on the previous instantiation [RFC1122], or
- If the use of the Timestamps option allows the disambiguation of segments from a previous instantiation to not otherwise be confused [RFC6191].
Uncommon Establishment and Termination
We notice that in 3-ways handshake, a device initiates the process, but it is possible to have TCP simultaneous open where both devices initiate the connection and both will then send ACK, forming 2 pairs of 2-way handshakes. This requires each end to have an IP address and port number that are known to the other end, which is not very common. In this case both play the role of active opener.
The TCP simultaneous close works in the same way which again involves 2 pairs of FIN and ACK packets, initiated by both hosts.
Additionally, if only 1 host wishes to terminate his end of connection, say he already finish sending data and only wants to receive, he can do so with shutdown() system call. In this case, only a pair of FIN+ACk and ACK packets are required. When the other hosts wish to close the other end, a pair of FIN and ACK packets is sent.