Chapter 3 ■ tCp
51
Processing text while splitting on fixed-length blocks would also not work for UTF-8 encoded Unicode data, since
a multibyte character could get split across a boundary between two of the binary blocks. In such cases, the server
would have to be more careful than in this example and carry some state between one block of data and the next.
In any case, handling input a block at a time like this is quite smart for the server, even if the 1,024-byte block size
used here for illustration is actually a very small value for today’s servers and networks. By handling the data in pieces
and immediately sending out responses, the server limits the amount of data that it has to keep in memory at any one
time. Servers designed like this could handle hundreds of clients at once, each sending streams totaling gigabytes,
without taxing memory or other hardware resources.
And for small data streams, the client and server in Listing 3-2 seem to work fine. If you start the server and then
run the client with a command-line argument specifying a modest number of bytes—say, asking it to send 32 bytes of
data—then it will get its text back in all uppercase. For simplicity, it will round whatever value you supply up to
a multiple of 16 bytes.
$ python tcp_deadlock.py client 127.0.0.1 32
Sending 32 bytes of data, in chunks of 16 bytes
32 bytes sent
Receiving all the data the server sends back
The first data received says b'CAPITALIZE THIS!CAPITALIZE THIS!'
32 bytes received
The server will report that it indeed processed 32 bytes on behalf of its recent client. The server, by the way, needs
to be running on the same machine, and this script uses the localhost IP address to make the example as simple as
possible.
Processing up to 1024 bytes at a time from ('127.0.0.1', 60461)
32 bytes processed so far
Socket closed
So, this code appears to work well when tested with small amounts of data. In fact, it might also work for larger
amounts. Try running the client with hundreds or thousands of bytes and see whether it continues to work.
This first example exchange of data, by the way, shows you the behavior of recv() that I have previously
described. Even though the server asked for 1,024 bytes to be received, recv(1024) was quite happy to return only
16 bytes if that was the amount of data that became available and no further data had yet arrived from the client.
But this client and server can be pushed into dire territory. If you try a large enough value, then disaster strikes!
Try using the client to send a large stream of data, say, one totaling a gigabyte.
$ python tcp_deadlock.py client 127.0.0.1 1073741824
You will see both the client and the server furiously updating their terminal windows as they breathlessly update
you with the amount of data they have transmitted and received. The numbers will climb and climb until, quite
suddenly, both connections freeze. Actually, if you watch carefully, you will see the server stop first, and then the