]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-112020: Rework socketserver examples to be correct (GH-129741) (#129744)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 6 Feb 2025 17:53:30 +0000 (18:53 +0100)
committerGitHub <noreply@github.com>
Thu, 6 Feb 2025 17:53:30 +0000 (17:53 +0000)
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.
(cherry picked from commit 78377c788e02e91bf43d290d69317198a2e563fd)

Co-authored-by: Gregory P. Smith <greg@krypto.org>
Doc/library/socketserver.rst

index f1f87ea975ca42754537e4f2a02986f6d3180b0a..e8d176edfcbda8dc123e51302f7c6d0b5c3f8ad9 100644 (file)
@@ -493,11 +493,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
@@ -514,20 +520,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::
@@ -542,13 +552,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:
@@ -593,7 +604,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)
 
@@ -618,8 +629,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.