Java Reference
In-Depth Information
The moral of the story: Design the protocol carefully to avoid sending large quantities of
data simultaneously in both directions.
Can this really happen? Let's review the compression protocol example in Section 4.5. Try
running the compression client with a large file that is still large after compression . The precise
definition of “large” here depends on your system, but a file that is already compressed and
exceeds 2MB should do nicely. For each read/write, the compression client prints an “R”/“W”
to the console. If both the uncompressed and compressed versions of the file are large enough,
your client will print a series of Ws and then stop without terminating or printing any Rs.
Why does this happen? The program CompressClient.java sends all of the uncompressed
data to the compression server before it attempts to read anything from the compressed
stream. The server, on the other hand, simply reads the uncompressed byte sequence and
writes the compressed sequence back to the client. (The number of bytes the server reads before
it writes some compressed data depends on the compression algorithm it uses.) Consider the
case where SendQ and RecvQ for both client and server hold 500 bytes each and the client
sends a 10,000-byte (uncompressed) file. Suppose also that for this file the server reads about
1000 bytes and then writes 500 bytes, for a 2:1 compression ratio. After the client sends 2000
bytes, the server will eventually have read them all and sent back 1000 bytes, and the client's
RecvQ and the server's SendQ will both be full. After the client sends another 1000 bytes and
the server reads them, the server's subsequent attempt to write will block. When the client
sends the next 1000 bytes, the client's SendQ and the server's RecvQ will both fill up. The next
client write will block, creating deadlock.
How do we solve this problem? The easiest solution is to execute the client writing and
reading loop in separate threads. One thread repeatedly reads a buffer of uncompressed bytes
from a file and sends them to the server until the end of the file is reached, whereupon it calls
shutdownOutput() on the socket. The other thread repeatedly reads a buffer of compressed
bytes from the server and writes them to the output file, until the input stream ends (i.e., the
server closes the socket). When one thread blocks, the other thread can proceed independently.
We can easily modify our client to follow this approach by putting the call to SendBytes() in
CompressClient.java inside a thread as follows:
Thread thread = new Thread() {
public void run() {
try {
SendBytes(sock, fileIn);
} catch (Exception ignored) {}
}
};
thread.start();
See CompressClientNoDeadlock.java on the topic's Web site for the complete example. Can
we solve this problem without using threads? To guarantee deadlock avoidance in a single
threaded solution, we need nonblocking writes, which are not available in the current version
of Java (see Section 4.2).
Search WWH ::




Custom Search