Skip to content

Commit 78377c7

Browse files
authored
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.
1 parent 51b4edb commit 78377c7

File tree

1 file changed

+28
-17
lines changed

1 file changed

+28
-17
lines changed

Doc/library/socketserver.rst

+28-17
Original file line numberDiff line numberDiff line change
@@ -499,11 +499,17 @@ This is the server side::
499499

500500
def handle(self):
501501
# self.request is the TCP socket connected to the client
502-
self.data = self.request.recv(1024).strip()
503-
print("Received from {}:".format(self.client_address[0]))
504-
print(self.data)
502+
pieces = [b'']
503+
total = 0
504+
while b'\n' not in pieces[-1] and total < 10_000:
505+
pieces.append(self.request.recv(2000))
506+
total += len(pieces[-1])
507+
self.data = b''.join(pieces)
508+
print(f"Received from {self.client_address[0]}:")
509+
print(self.data.decode("utf-8"))
505510
# just send back the same data, but upper-cased
506511
self.request.sendall(self.data.upper())
512+
# after we return, the socket will be closed.
507513

508514
if __name__ == "__main__":
509515
HOST, PORT = "localhost", 9999
@@ -520,20 +526,24 @@ objects that simplify communication by providing the standard file interface)::
520526
class MyTCPHandler(socketserver.StreamRequestHandler):
521527

522528
def handle(self):
523-
# self.rfile is a file-like object created by the handler;
524-
# we can now use e.g. readline() instead of raw recv() calls
525-
self.data = self.rfile.readline().strip()
526-
print("{} wrote:".format(self.client_address[0]))
527-
print(self.data)
529+
# self.rfile is a file-like object created by the handler.
530+
# We can now use e.g. readline() instead of raw recv() calls.
531+
# We limit ourselves to 10000 bytes to avoid abuse by the sender.
532+
self.data = self.rfile.readline(10000).rstrip()
533+
print(f"{self.client_address[0]} wrote:")
534+
print(self.data.decode("utf-8"))
528535
# Likewise, self.wfile is a file-like object used to write back
529536
# to the client
530537
self.wfile.write(self.data.upper())
531538

532539
The difference is that the ``readline()`` call in the second handler will call
533540
``recv()`` multiple times until it encounters a newline character, while the
534-
single ``recv()`` call in the first handler will just return what has been
535-
received so far from the client's ``sendall()`` call (typically all of it, but
536-
this is not guaranteed by the TCP protocol).
541+
the first handler had to use a ``recv()`` loop to accumulate data until a
542+
newline itself. If it had just used a single ``recv()`` without the loop it
543+
would just have returned what has been received so far from the client.
544+
TCP is stream based: data arrives in the order it was sent, but there no
545+
correlation between client ``send()`` or ``sendall()`` calls and the number
546+
of ``recv()`` calls on the server required to receive it.
537547

538548

539549
This is the client side::
@@ -548,13 +558,14 @@ This is the client side::
548558
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
549559
# Connect to server and send data
550560
sock.connect((HOST, PORT))
551-
sock.sendall(bytes(data + "\n", "utf-8"))
561+
sock.sendall(bytes(data, "utf-8"))
562+
sock.sendall(b"\n")
552563

553564
# Receive data from the server and shut down
554565
received = str(sock.recv(1024), "utf-8")
555566

556-
print("Sent: {}".format(data))
557-
print("Received: {}".format(received))
567+
print("Sent: ", data)
568+
print("Received:", received)
558569

559570

560571
The output of the example should look something like this:
@@ -599,7 +610,7 @@ This is the server side::
599610
def handle(self):
600611
data = self.request[0].strip()
601612
socket = self.request[1]
602-
print("{} wrote:".format(self.client_address[0]))
613+
print(f"{self.client_address[0]} wrote:")
603614
print(data)
604615
socket.sendto(data.upper(), self.client_address)
605616

@@ -624,8 +635,8 @@ This is the client side::
624635
sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))
625636
received = str(sock.recv(1024), "utf-8")
626637

627-
print("Sent: {}".format(data))
628-
print("Received: {}".format(received))
638+
print("Sent: ", data)
639+
print("Received:", received)
629640

630641
The output of the example should look exactly like for the TCP server example.
631642

0 commit comments

Comments
 (0)