gh-112020: Rework socketserver examples to be correct (#129741)

gh-112020: Rework socketserver examples to be correct.

Outdated code updated, the BaseRequestHandler example is now much more
illustrative instead of the bad idea of a single recv() call for TCP.

tested, they now work.
This commit is contained in:
Gregory P. Smith 2025-02-06 09:42:47 -08:00 committed by GitHub
parent 51b4edb1a4
commit 78377c788e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -499,11 +499,17 @@ This is the server side::
def handle(self):
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
print("Received from {}:".format(self.client_address[0]))
print(self.data)
pieces = [b'']
total = 0
while b'\n' not in pieces[-1] and total < 10_000:
pieces.append(self.request.recv(2000))
total += len(pieces[-1])
self.data = b''.join(pieces)
print(f"Received from {self.client_address[0]}:")
print(self.data.decode("utf-8"))
# just send back the same data, but upper-cased
self.request.sendall(self.data.upper())
# after we return, the socket will be closed.
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
@ -520,20 +526,24 @@ objects that simplify communication by providing the standard file interface)::
class MyTCPHandler(socketserver.StreamRequestHandler):
def handle(self):
# self.rfile is a file-like object created by the handler;
# we can now use e.g. readline() instead of raw recv() calls
self.data = self.rfile.readline().strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
# self.rfile is a file-like object created by the handler.
# We can now use e.g. readline() instead of raw recv() calls.
# We limit ourselves to 10000 bytes to avoid abuse by the sender.
self.data = self.rfile.readline(10000).rstrip()
print(f"{self.client_address[0]} wrote:")
print(self.data.decode("utf-8"))
# Likewise, self.wfile is a file-like object used to write back
# to the client
self.wfile.write(self.data.upper())
The difference is that the ``readline()`` call in the second handler will call
``recv()`` multiple times until it encounters a newline character, while the
single ``recv()`` call in the first handler will just return what has been
received so far from the client's ``sendall()`` call (typically all of it, but
this is not guaranteed by the TCP protocol).
the first handler had to use a ``recv()`` loop to accumulate data until a
newline itself. If it had just used a single ``recv()`` without the loop it
would just have returned what has been received so far from the client.
TCP is stream based: data arrives in the order it was sent, but there no
correlation between client ``send()`` or ``sendall()`` calls and the number
of ``recv()`` calls on the server required to receive it.
This is the client side::
@ -548,13 +558,14 @@ This is the client side::
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
# Connect to server and send data
sock.connect((HOST, PORT))
sock.sendall(bytes(data + "\n", "utf-8"))
sock.sendall(bytes(data, "utf-8"))
sock.sendall(b"\n")
# Receive data from the server and shut down
received = str(sock.recv(1024), "utf-8")
print("Sent: {}".format(data))
print("Received: {}".format(received))
print("Sent: ", data)
print("Received:", received)
The output of the example should look something like this:
@ -599,7 +610,7 @@ This is the server side::
def handle(self):
data = self.request[0].strip()
socket = self.request[1]
print("{} wrote:".format(self.client_address[0]))
print(f"{self.client_address[0]} wrote:")
print(data)
socket.sendto(data.upper(), self.client_address)
@ -624,8 +635,8 @@ This is the client side::
sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))
received = str(sock.recv(1024), "utf-8")
print("Sent: {}".format(data))
print("Received: {}".format(received))
print("Sent: ", data)
print("Received:", received)
The output of the example should look exactly like for the TCP server example.