Sockets: Advanced Topics 1273
As with the SYN flag, and for the same reasons, the FIN flag consumes a byte of the
sequence-number space for the connection. This is why we show the acknowledge-
ment of the FIN M segment as ACK M+1 in Figure 61-6.
Figure 61-6: TCP connection termination
61.6.6 Calling shutdown() on a TCP Socket
The discussion in the preceding section assumed a full-duplex close; that is, an
application closes both the sending and receiving channels of the TCP socket using
close(). As noted in Section 61.2, we can use shutdown() to close just one channel of
the connection (a half-duplex close). This section notes some specific details for
shutdown() on a TCP socket.
Specifying how as SHUT_WR or SHUT_RDWR initiates the TCP connection termination
sequence (i.e., the active close) described in Section 61.6.5, regardless of whether there
are other file descriptors referring to the socket. Once this sequence has been initiated,
the local TCP moves into the FIN_WAIT1 state, and then into the FIN_WAIT2 state,
while the peer TCP moves into the CLOSE_WAIT state (Figure 61-6). If how is speci-
fied as SHUT_WR, then, since the socket file descriptor remains valid and the reading
half of the connection remains open, the peer can continue to send data back to us.
The SHUT_RD operation can’t be meaningfully used with TCP sockets. This is
because most TCP implementations don’t provide the expected behavior for
SHUT_RD, and the effect of SHUT_RD varies across implementations. On Linux and a few
other implementations, following a SHUT_RD (and after any outstanding data has
been read), a read() returns end-of-file, as we expect from the description of SHUT_RD
in Section 61.2. However, if the peer application subsequently writes data on its
socket, then it is still possible to read that data on the local socket.
On some other implementations (e.g., the BSDs), SHUT_RD does indeed cause
subsequent calls to read() to always return 0. However, on those implementations,
if the peer continues to write() to the socket, then the data channel will eventually
fill until the point where a further (blocking) call to write() by the peer will block.
(With UNIX domain stream sockets, a peer would receive a SIGPIPE signal and the
EPIPE error if it continued writing to its socket after a SHUT_RD had been performed
on the local socket.)
In summary, the use of SHUT_RD should be avoided for portable TCP applications.
FIN M
ACK M+1
Client
ACK N+1
close()
(active close)
<CLOSE_WAIT>
<LAST_ACK>
<ESTABLISHED> <ESTABLISHED>
<FIN_WAIT1>
FIN N
<FIN_WAIT2>
<TIME_WAIT>
Server
(passive close)
close()
<CLOSED>