Chapter 3 ■ tCp
49
This limitation will generally not trouble you if you follow the client-server pattern shown in Listing 3-1, where
each end always reads its partner’s complete message before turning around and sending data in the other direction.
But you can run into trouble quickly if you design a client and server that leave too much data waiting without having
some arrangement for promptly reading it.
Take a look at Listing 3-2 for an example of a server and client that try to be a bit too clever without thinking
through the consequences. Here the server author has done something that is actually quite intelligent. The server’s
job is to turn an arbitrary amount of text into uppercase. Recognizing that client requests can be arbitrarily large and
that one could run out of memory trying to read an entire stream of input before trying to process it, the server reads
and processes small blocks of 1,024 bytes of data at a time.
Listing 3-2. TCP Server and Client That Can Deadlock
#!/usr/bin/env python3
Foundations of Python Network Programming, Third Edition
https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter03/tcp_deadlock.py
TCP client and server that leave too much data waiting
import argparse, socket, sys
def server(host, port, bytecount):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port))
sock.listen(1)
print('Listening at', sock.getsockname())
while True:
sc, sockname = sock.accept()
print('Processing up to 1024 bytes at a time from', sockname)
n = 0
while True:
data = sc.recv(1024)
if not data:
break
output = data.decode('ascii').upper().encode('ascii')
sc.sendall(output) # send it back uppercase
n += len(data)
print('\r %d bytes processed so far' % (n,), end=' ')
sys.stdout.flush()
print()
sc.close()
print(' Socket closed')
def client(host, port, bytecount):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
bytecount = (bytecount + 15) // 16 * 16 # round up to a multiple of 16
message = b'capitalize this!' # 16-byte message to repeat over and over
print('Sending', bytecount, 'bytes of data, in chunks of 16 bytes')
sock.connect((host, port))