■
4.6 Closing Connections 139
close from the client, it immediately closes. In effect, the client close indicates that the
communication is completed. Basic HTTP works the same way, except that the server is
the terminator.
6
Let’s consider a different protocol. Suppose you want a transcoding server that takes
a stream of bytes in Unicode, converts them to UTF-8, and sends the UTF-8 stream back
to the client. Which endpoint should close the connection? Since the stream of bytes from
the client is arbitrarily long, the client needs to close the connection so that the server
knows when the stream of bytes to be encoded ends. When should the client call Close()?
If the client calls Close() on the socket immediately after it sends the last byte of data, it
will not be able to receive the last bytes of UTF-8 data. Perhaps the client could wait until
it receives all of the UTF-8 data before it closes, as the echo protocol does. Unfortunately,
neither the server nor the client knows how many bytes to expect since UTF-8 encoding is
of variable length (see Section 3.1.1), so this will not work either. What is needed is a way
to tell the other end of the connection “I am through sending,” without losing the ability
to receive.
Fortunately, sockets provide a way to do this. The Shutdown() method of Socket
allows the I/O streams to be closed independently. The Shutdown() method takes as an
argument an instance of the SocketShutdown enumeration, which can have the values Send,
Receive,orBoth. After a call to Shutdown(SocketShutdown.Receive), the socket can no
longer receive input. Any undelivered data is silently discarded, and any attempt to read
from the socket will generate a SocketException. After Shutdown(SocketShutdown.Send) is
called on a Socket, no more data may be sent on the socket. Attempts to write to the stream
also throw a SocketException. Any data written before the call to Shutdown(SocketShut-
down.Send) may be read by the remote socket. After this, a read on the input stream of
the remote socket will return 0. An application calling Shutdown(SocketShutdown.Send)
can continue to read from the socket and, similarly, data can be written after calling
Shutdown(SocketShutdown.Receive).
In the Transcode protocol (see Figure 4.3), the client writes the Unicode bytes, clos-
ing the output stream using Shutdown(SocketShutdown.Send) when finished sending, and
reads the UTF-8 byte stream from the server. The server repeatedly reads the Unicode
data and writes the UTF-8 data until the client performs a shutdown, causing the server
read to return 0, indicating an end-of-stream. The server then closes the connection
and exits. After the client calls Shutdown(SocketShutdown.Send), it needs to read any
remaining UTF-8 bytes from the server.
Our client, TranscodeClient.cs, implements the client side of the Transcode pro-
tocol. The Unicode bytes are read from the file specified on the command line, and the
UTF-8 bytes are written to a new file. If the Unicode filename is “data,” the UTF-8 file
name is “data.ut8.” Note that this implementation works for small files, but that there is
a flaw that causes deadlock for large files. (We discuss and correct this shortcoming in
Section 5.2.)
6
More sophisticated features of HTTP, such as persistent connections, are quite common today and
operate differently.