import optparse import io import socket import sys import time def ClientRepro(port): def EchoChunk(c, data): l = len(data) c.send(str(l).encode() + b'\r\n') c.send(data) print("Asking for {0} bytes of data.".format(l)) response = c.recv(l) if data != response: raise UserWarning('Echoed data does not match.') else: print("Successfully echoed {0} byte chunk.".format(l)) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('127.0.0.1', port)) for chunkSize in ( 1, 2, 4, 16, 32, 64, 100, 1000, 2000, 20000 ): EchoChunk(s, ('A'*chunkSize).encode()) # Works EchoChunk(s, ('A'*io.DEFAULT_BUFFER_SIZE).encode()) # Server Hangs s.close() def ServerRepro(port): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('127.0.0.1', port)) s.listen(1) connection, addr = s.accept() # We create a file object from an underlying blocking socket. # f = connection.makefile('rb') while True: lengthLine = f.readline() if not lengthLine: break length = int(lengthLine.strip()) if length: print("Reading {0} byte chunk.".format(length)) # The bug is triggered here, when we've previously # received some data (a buffer exists for the input,) # and we request to read an exact multiple of the block # size (or more correctly our final read is a single # block that finishes exactly at the end of the input # data) -- this causes the BufferedReader to attempt # one more read on the socket, which will block # forever. # data = f.read(int(length)) print("Echoing {0} byte chunk.".format(length)) connection.send(data) connection.close() parser = optparse.OptionParser() parser.add_option('-p', '--port', default=12345, type=int, help='Specify the client/server TCP port to use.') ( opt, arg ) = parser.parse_args() if len(arg) != 1: parser.print_usage() sys.exit() if arg[0] == 'client': ClientRepro(opt.port) elif arg[0] == 'server': ServerRepro(opt.port) else: raise UserWarning('Argument must be either "client" or "server"')