Skip to content

Commit 7ff8e8d

Browse files
[3.12] gh-112020: Rework socketserver examples to be correct (GH-129741) (#129744)
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 78377c7) Co-authored-by: Gregory P. Smith <[email protected]>
1 parent de93805 commit 7ff8e8d

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
@@ -493,11 +493,17 @@ This is the server side::
493493

494494
def handle(self):
495495
# self.request is the TCP socket connected to the client
496-
self.data = self.request.recv(1024).strip()
497-
print("Received from {}:".format(self.client_address[0]))
498-
print(self.data)
496+
pieces = [b'']
497+
total = 0
498+
while b'\n' not in pieces[-1] and total < 10_000:
499+
pieces.append(self.request.recv(2000))
500+
total += len(pieces[-1])
501+
self.data = b''.join(pieces)
502+
print(f"Received from {self.client_address[0]}:")
503+
print(self.data.decode("utf-8"))
499504
# just send back the same data, but upper-cased
500505
self.request.sendall(self.data.upper())
506+
# after we return, the socket will be closed.
501507

502508
if __name__ == "__main__":
503509
HOST, PORT = "localhost", 9999
@@ -514,20 +520,24 @@ objects that simplify communication by providing the standard file interface)::
514520
class MyTCPHandler(socketserver.StreamRequestHandler):
515521

516522
def handle(self):
517-
# self.rfile is a file-like object created by the handler;
518-
# we can now use e.g. readline() instead of raw recv() calls
519-
self.data = self.rfile.readline().strip()
520-
print("{} wrote:".format(self.client_address[0]))
521-
print(self.data)
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+
# We limit ourselves to 10000 bytes to avoid abuse by the sender.
526+
self.data = self.rfile.readline(10000).rstrip()
527+
print(f"{self.client_address[0]} wrote:")
528+
print(self.data.decode("utf-8"))
522529
# Likewise, self.wfile is a file-like object used to write back
523530
# to the client
524531
self.wfile.write(self.data.upper())
525532

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

532542

533543
This is the client side::
@@ -542,13 +552,14 @@ This is the client side::
542552
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
543553
# Connect to server and send data
544554
sock.connect((HOST, PORT))
545-
sock.sendall(bytes(data + "\n", "utf-8"))
555+
sock.sendall(bytes(data, "utf-8"))
556+
sock.sendall(b"\n")
546557

547558
# Receive data from the server and shut down
548559
received = str(sock.recv(1024), "utf-8")
549560

550-
print("Sent: {}".format(data))
551-
print("Received: {}".format(received))
561+
print("Sent: ", data)
562+
print("Received:", received)
552563

553564

554565
The output of the example should look something like this:
@@ -593,7 +604,7 @@ This is the server side::
593604
def handle(self):
594605
data = self.request[0].strip()
595606
socket = self.request[1]
596-
print("{} wrote:".format(self.client_address[0]))
607+
print(f"{self.client_address[0]} wrote:")
597608
print(data)
598609
socket.sendto(data.upper(), self.client_address)
599610

@@ -618,8 +629,8 @@ This is the client side::
618629
sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))
619630
received = str(sock.recv(1024), "utf-8")
620631

621-
print("Sent: {}".format(data))
622-
print("Received: {}".format(received))
632+
print("Sent: ", data)
633+
print("Received:", received)
623634

624635
The output of the example should look exactly like for the TCP server example.
625636

0 commit comments

Comments
 (0)