Chapter 5 ■ Network Data aND Network errors
88
Note that most compression schemes, when given tiny payloads, tend to make them longer instead of shorter
because the overhead of the compression format overwhelms any tiny bit of compressibility in the payload.
Imagine that these 28 bytes arrive at their destination in 8-byte packets. After processing the first packet, you will
find the decompression object’s unused_data slot still empty, telling you that there is still more data coming.
d = zlib.decompressobj()
d.decompress(data[0:8]), d.unused_data
(b'Pytho', b'')
So, you would want to recv() on the socket again. The second block of eight characters, when fed to the
decompress object, will both finish out the compressed data for which you were waiting and return a nonempty
unused_data value that shows you that you finally received the b'.' byte:
d.decompress(data[8:16]), d.unused_data
('n', '.x')
The character following the period must be the first byte of whatever payload is coming after this first bit of
compressed data. Since here you are expecting further compressed data, you will feed the 'x' to a fresh decompress
object to which you can then feed the final 8-byte “packets” you are simulating:
d = zlib.decompressobj()
d.decompress(b'x'), d.unused_data
(b'', b'')
d.decompress(data[16:24]), d.unused_data
(b'zlib', b'')
d.decompress(data[24:]), d.unused_data
(b'', b'.')
At this point, unused_data is again nonempty to show you that you have read past the end of this second bout of
compressed data and can examine its content knowing that it has arrived complete and intact.
Again, most protocol designers make compression optional and simply do their own framing. Nonetheless, if you
know ahead of time that you will always want to use zlib, then a convention like this will let you take advantage of the
stream termination built into zlib and autodetect the end of each compressed stream.
Network Exceptions
The example scripts in this book are generally designed to catch only those exceptions that are integral to the feature
being demonstrated. Thus, when I illustrated socket timeouts in Listing 2-2, I was careful to catch the exception
socket.timeout since that is how timeouts are signaled. However, I ignored all of the other exceptions that will occur
if the hostname provided on the command line is invalid, a remote IP is used with bind(), the port used with bind()
is already busy, or the peer cannot be contacted or stops responding.
What errors can result from working with sockets? Though the number of errors that can take place while using a
network connection is quite large—involving every possible misstep that can occur at every stage of the complex
TCP/IP protocol—the number of actual exceptions with which socket operations can hit your programs is fortunately
quite few. The exceptions that are specific to socket operations are as follows:
OSError: This is the workhorse of the socket module, and it will be raised for nearly every
failure that can happen at any stage in network transmission. This can occur during nearly
any socket call, even when you least expect it. When a previous send(), for example, has
elicited a reset (RST) packet from the remote host, you will actually see the error raised by
whatever socket operation you next attempt on that socket.