Foundations of Python Network Programming

(WallPaper) #1
Chapter 5 ■ Network Data aND Network errors

85

sock.listen(1)
print('Run this script in another window with "-c" to connect')
print('Listening at', sock.getsockname())
sc, sockname = sock.accept()
print('Accepted connection from', sockname)
sc.shutdown(socket.SHUT_WR)
while True:
block = get_block(sc)
if not block:
break
print('Block says:', repr(block))
sc.close()
sock.close()


def client(address):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(address)
sock.shutdown(socket.SHUT_RD)
put_block(sock, b'Beautiful is better than ugly.')
put_block(sock, b'Explicit is better than implicit.')
put_block(sock, b'Simple is better than complex.')
put_block(sock, b'')
sock.close()


if name == 'main':
parser = ArgumentParser(description='Transmit & receive blocks over TCP')
parser.add_argument('hostname', nargs='?', default='127.0.0.1',
help='IP address or hostname (default: %(default)s)')
parser.add_argument('-c', action='store_true', help='run as the client')
parser.add_argument('-p', type=int, metavar='port', default=1060,
help='TCP port number (default: %(default)s)')
args = parser.parse_args()
function = client if args.c else server
function((args.hostname, args.p))


Note how careful you must be! Even though the 4-byte length field is such a tiny amount of data that you might
not be able to imagine recv() not returning it all at once, the code is correct only if you carefully wrap recv() in a loop
that (just in case) will keep demanding more data until all four bytes have arrived. This is the kind of caution that is
necessary when writing network code.
Thus, you have at least six options for dividing up an unending stream of data into digestible chunks so that
clients and servers know when a message is complete and can turn around and respond. Note that many modern
protocols mix them together, and you are free to do the same thing.
A good example of a mashup between different framing techniques is the HTTP protocol, which you will
learn more about later in this book. It uses a delimiter—the blank line '\r\n\r\n'—to signal when its headers are
complete. Because the headers are text, line endings can safely be treated as special characters this way. However,
since the actual payload can be pure binary data, such as an image or compressed file, a Content-Length measured in
bytes is provided in the headers to determine how much more data to read off the socket past the end of the headers.
Thus, HTTP mixes the fourth and fifth patterns you have seen here. In fact, it can also use the sixth option: if a server is
streaming a response whose length it cannot predict, then HTTP can use a “chunked encoding,” which sends a series
of blocks that are each prefixed with their length. A zero-length field marks the end of the transmission, just as it does
in Listing 5-2.

Free download pdf