Preventing lost and delayed packets affecting future connections is only a secondary purpose of
TIME_WAIT1. TCP is hardened against such packets by virtue of the receive window and, in modern implementations, timestamps.
TIME_WAIT's primary purpose is to handle the case in which the final
ACK is lost. In such a case the final
FIN will be re-received and the final
ACK will need to be re-transmitted. This is necessary because there is no
ACK, or the two endpoints would be
ACKing each other forever. Instead, one side sends the final
ACK and waits for some time in
TIME_WAIT so as to be fairly certain that the other endpoint received said
ACK and did not re-transmit its
However, the fact that the endpoint that passively closes does not enter a state equivalent to
TIME_WAIT I view as a design flaw in TCP. Have a look at the TCP state diagram in this page, and consider the case where the second last
ACK is lost:
Green: State | Blue: API calls | Red: errors, events and notes | Purple line: connection state save-points
The problem is that the two Endpoints get out of sync if the second-last
ACK is lost: Endpoint A believes a simultaneous close is happening and awaits a final
ACK, whereas Endpoint B believes that the connection is fully closed both ways (Diagram 1).
This eventually cleans itself up if the connection is left as-is (Diagram 2), but nothing stops Endpoint B from trying to re-open the connection (Diagram 3) which results in
connection reset errors (Diagrams 4 and 5).
A better design would have been to merge the
LAST_ACK states so that both ends go through
TIME_WAIT, at the cost of slightly more memory usage at the passively closing end.
1No direct mention of
TIME_WAIT's secondary purpose of preventing segments from an old incarnation of the connection from interfering with a new incarnation is made in RFC 739, and can be only vaguely inferred from the sections on quiet time and section 22.214.171.124 of RFC 1122. I notice that RFC 6528 Section 2 from 2012 states:
It is interesting to note that, as a matter of fact, protection
against stale segments from a previous incarnation of the connection
is enforced by preventing the creation of a new incarnation of a
previous connection before 2*MSL have passed since a segment
corresponding to the old incarnation was last seen (where "MSL" is
the "Maximum Segment Lifetime" [RFC0793]). This is accomplished by
the TIME-WAIT state and TCP's "quiet time" concept
However I find this to be incorrect as a connection that is reset does not enter the
TIME_WAIT state and instead enters the
LISTEN states. So old segments could still be in flight at a time when the connection could be re-opened. This could perhaps be remedied by having reset connections go into a
TIME_WAIT-like state. The confusion over this dual-purpose may be the root cause of these inconsistencies.