socketserver — Ein Framework für Netzwerksrealser

Quellcode: Lib/socketserver.py


Das Modul socketserver vereinfacht die Aufgabe der Erstellung von Netzwerksrealsern.

Verfügbarkeit: nicht WASI.

Dieses Modul funktioniert nicht oder ist nicht auf WebAssembly verfügbar. Weitere Informationen finden Sie unter WebAssembly-Plattformen.

Es gibt vier grundlegende konkrete Serverklassen

class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)

Dies verwendet das Internet-TCP-Protokoll, das für kontinuierliche Datenströme zwischen Client und Server sorgt. Wenn bind_and_activate wahr ist, versucht der Konstruktor automatisch, server_bind() und server_activate() aufzurufen. Die anderen Parameter werden an die Basisklasse BaseServer übergeben.

class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)

Dies verwendet Datagramme, die diskrete Informationspakete sind, die außer Reihenfolge ankommen oder während der Übertragung verloren gehen können. Die Parameter sind dieselben wie für TCPServer.

class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)
class socketserver.UnixDatagramServer(server_address, RequestHandlerClass, bind_and_activate=True)

Diese seltener verwendeten Klassen ähneln den TCP- und UDP-Klassen, verwenden jedoch Unix-Domain-Sockets; sie sind auf Nicht-Unix-Plattformen nicht verfügbar. Die Parameter sind dieselben wie für TCPServer.

Diese vier Klassen verarbeiten Anfragen synchron; jede Anfrage muss abgeschlossen sein, bevor die nächste Anfrage begonnen werden kann. Dies ist nicht geeignet, wenn jede Anfrage lange zur Fertigstellung benötigt, weil sie viel Berechnung erfordert oder weil sie viele Daten zurückgibt, die der Client nur langsam verarbeiten kann. Die Lösung besteht darin, einen separaten Prozess oder Thread zu erstellen, um jede Anfrage zu bearbeiten; die Mix-in-Klassen ForkingMixIn und ThreadingMixIn können verwendet werden, um asynchrones Verhalten zu unterstützen.

Die Erstellung eines Servers erfordert mehrere Schritte. Zuerst müssen Sie eine Anfragebehandlungsklasse erstellen, indem Sie von der Klasse BaseRequestHandler erben und ihre Methode handle() überschreiben; diese Methode wird eingehende Anfragen bearbeiten. Zweitens müssen Sie eine der Serverklassen instanziieren und ihr die Adresse des Servers und die Anfragebehandlungsklasse übergeben. Es wird empfohlen, den Server in einer with-Anweisung zu verwenden. Rufen Sie dann die Methode handle_request() oder serve_forever() des Serverobjekts auf, um eine oder mehrere Anfragen zu bearbeiten. Schließlich rufen Sie server_close() auf, um den Socket zu schließen (es sei denn, Sie haben eine with-Anweisung verwendet).

Wenn Sie von ThreadingMixIn für das Verhalten von Thread-Verbindungen erben, sollten Sie explizit deklarieren, wie Ihre Threads bei einem abrupten Herunterfahren reagieren sollen. Die Klasse ThreadingMixIn definiert ein Attribut daemon_threads, das angibt, ob der Server auf die Thread-Beendigung warten soll. Sie sollten das Flag explizit setzen, wenn Sie möchten, dass Threads autonom agieren; der Standardwert ist False, was bedeutet, dass Python erst beendet wird, wenn alle von ThreadingMixIn erstellten Threads beendet wurden.

Serverklassen haben dieselben externen Methoden und Attribute, unabhängig davon, welches Netzwerkprotokoll sie verwenden.

Hinweise zur Servererstellung

Es gibt fünf Klassen in einem Vererbungsdiagramm, von denen vier synchrone Server von vier Typen darstellen

+------------+
| BaseServer |
+------------+
      |
      v
+-----------+        +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+        +------------------+
      |
      v
+-----------+        +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+        +--------------------+

Beachten Sie, dass UnixDatagramServer von UDPServer abgeleitet ist, und nicht von UnixStreamServer — der einzige Unterschied zwischen einem IP- und einem Unix-Server ist die Adressfamilie.

class socketserver.ForkingMixIn
class socketserver.ThreadingMixIn

Forking- und Threading-Versionen jeder Serverart können mit diesen Mix-in-Klassen erstellt werden. Zum Beispiel wird ThreadingUDPServer wie folgt erstellt

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

Die Mix-in-Klasse kommt zuerst, da sie eine in UDPServer definierte Methode überschreibt. Das Setzen der verschiedenen Attribute ändert auch das Verhalten des zugrunde liegenden Servermechanismus.

ForkingMixIn und die unten genannten Forking-Klassen sind nur auf POSIX-Plattformen verfügbar, die fork() unterstützen.

block_on_close

ForkingMixIn.server_close wartet, bis alle Kindprozesse abgeschlossen sind, es sei denn, das Attribut block_on_close ist False.

ThreadingMixIn.server_close wartet, bis alle nicht-daemonischen Threads abgeschlossen sind, es sei denn, das Attribut block_on_close ist False.

max_children

Geben Sie an, wie viele Kindprozesse gleichzeitig Anfragen für ForkingMixIn bearbeiten sollen. Wenn das Limit erreicht ist, warten neue Anfragen, bis ein Kindprozess beendet wurde.

daemon_threads

Verwenden Sie für ThreadingMixIn daemontische Threads, indem Sie ThreadingMixIn.daemon_threads auf True setzen, um nicht auf den Abschluss von Threads zu warten.

Geändert in Version 3.7: ForkingMixIn.server_close und ThreadingMixIn.server_close warten jetzt, bis alle Kindprozesse und nicht-daemonischen Threads abgeschlossen sind. Ein neues Klassenattribut ForkingMixIn.block_on_close wurde hinzugefügt, um das Verhalten vor Version 3.7 zu aktivieren.

class socketserver.ForkingTCPServer
class socketserver.ForkingUDPServer
class socketserver.ThreadingTCPServer
class socketserver.ThreadingUDPServer
class socketserver.ForkingUnixStreamServer
class socketserver.ForkingUnixDatagramServer
class socketserver.ThreadingUnixStreamServer
class socketserver.ThreadingUnixDatagramServer

Diese Klassen sind vordefiniert und verwenden die Mix-in-Klassen.

Hinzugefügt in Version 3.12: Die Klassen ForkingUnixStreamServer und ForkingUnixDatagramServer wurden hinzugefügt.

Um einen Dienst zu implementieren, müssen Sie eine Klasse von BaseRequestHandler ableiten und ihre Methode handle() neu definieren. Sie können dann verschiedene Versionen des Dienstes ausführen, indem Sie eine der Serverklassen mit Ihrer Anfragebehandlungsklasse kombinieren. Die Anfragebehandlungsklasse muss für Datagramm- oder Stream-Dienste unterschiedlich sein. Dies kann durch die Verwendung der Handler-Unterklassen StreamRequestHandler oder DatagramRequestHandler verborgen werden.

Natürlich müssen Sie immer noch Ihren Verstand benutzen! Zum Beispiel ist es unsinnig, einen Forking-Server zu verwenden, wenn der Dienst einen Zustand im Speicher enthält, der von verschiedenen Anfragen geändert werden kann, da die Änderungen im Kindprozess nie den ursprünglichen Zustand im Elternprozess erreichen und an jedes Kind weitergegeben würden. In diesem Fall können Sie einen Threading-Server verwenden, aber Sie müssen wahrscheinlich Sperren verwenden, um die Integrität der gemeinsamen Daten zu schützen.

Wenn Sie hingegen einen HTTP-Server erstellen, bei dem alle Daten extern gespeichert sind (z.B. im Dateisystem), macht eine synchrone Klasse den Dienst im Wesentlichen "taub", während eine Anfrage bearbeitet wird – was sehr lange dauern kann, wenn ein Client die angeforderten Daten nur langsam empfängt. Hier ist ein Threading- oder Forking-Server angebracht.

In einigen Fällen kann es angebracht sein, einen Teil einer Anfrage synchron zu verarbeiten, die Verarbeitung aber in einem geforkten Kindprozess abzuschließen, abhängig von den Anfragedaten. Dies kann implementiert werden, indem ein synchroner Server verwendet und in der Methode handle() der Anfragebehandlungsklasse explizit geforkt wird.

Ein anderer Ansatz zur Verarbeitung mehrerer gleichzeitiger Anfragen in einer Umgebung, die weder Threads noch fork() unterstützt (oder wo diese zu teuer oder für den Dienst ungeeignet sind), besteht darin, eine explizite Tabelle von teilweise abgeschlossenen Anfragen zu pflegen und selectors zu verwenden, um zu entscheiden, welche Anfrage als nächstes bearbeitet werden soll (oder ob eine neue eingehende Anfrage bearbeitet werden soll). Dies ist besonders wichtig für Stream-Dienste, bei denen jeder Client potenziell lange verbunden sein kann (wenn keine Threads oder Unterprozesse verwendet werden können).

Serverobjekte

class socketserver.BaseServer(server_address, RequestHandlerClass)

Dies ist die Oberklasse aller Serverobjekte im Modul. Sie definiert die unten angegebene Schnittstelle, implementiert aber nicht die meisten Methoden, was in Unterklassen geschieht. Die beiden Parameter werden in den entsprechenden Attributen server_address und RequestHandlerClass gespeichert.

fileno()

Gibt einen ganzzahligen Dateideskriptor für den Socket zurück, auf dem der Server lauscht. Diese Funktion wird am häufigsten an selectors übergeben, um das Überwachen mehrerer Server im selben Prozess zu ermöglichen.

handle_request()

Verarbeitet eine einzelne Anfrage. Diese Funktion ruft die folgenden Methoden in der Reihenfolge auf: get_request(), verify_request() und process_request(). Wenn die vom Benutzer bereitgestellte Methode handle() der Handler-Klasse eine Ausnahme auslöst, wird die Methode handle_error() des Servers aufgerufen. Wenn innerhalb von timeout Sekunden keine Anfrage empfangen wird, wird handle_timeout() aufgerufen und handle_request() kehrt zurück.

serve_forever(poll_interval=0.5)

Bearbeitet Anfragen, bis eine explizite shutdown()-Anfrage empfangen wird. Fragt alle poll_interval Sekunden nach der Beendigung ab. Ignoriert das Attribut timeout. Es ruft auch service_actions() auf, das von einer Unterklasse oder einem Mix-in verwendet werden kann, um dienstspezifische Aktionen bereitzustellen. Beispielsweise verwendet die Klasse ForkingMixIn service_actions(), um Zombie-Kindprozesse aufzuräumen.

Geändert in Version 3.3: Der Aufruf von service_actions wurde zur Methode serve_forever hinzugefügt.

service_actions()

Dies wird in der Schleife von serve_forever() aufgerufen. Diese Methode kann von Unterklassen oder Mix-in-Klassen überschrieben werden, um dienstspezifische Aktionen auszuführen, wie z.B. Aufräumarbeiten.

Hinzugefügt in Version 3.3.

shutdown()

Weist die Schleife von serve_forever() an, anzuhalten und zu warten, bis sie dies tut. shutdown() muss aufgerufen werden, während serve_forever() in einem anderen Thread läuft, sonst kommt es zu einem Deadlock.

server_close()

Bereinigt den Server. Kann überschrieben werden.

address_family

Die Protokollfamilie, zu der der Socket des Servers gehört. Häufige Beispiele sind socket.AF_INET, socket.AF_INET6 und socket.AF_UNIX. Erben Sie von den TCP- oder UDP-Serverklassen in diesem Modul mit dem auf AF_INET6 gesetzten Klassenattribut address_family = AF_INET6, wenn Sie IPv6-Serverklassen wünschen.

RequestHandlerClass

Die vom Benutzer bereitgestellte Anfragebehandlungsklasse; eine Instanz dieser Klasse wird für jede Anfrage erstellt.

server_address

Die Adresse, auf der der Server lauscht. Das Format der Adressen hängt von der Protokollfamilie ab; siehe die Dokumentation des Moduls socket für Details. Für Internetprotokolle ist dies ein Tupel, das einen String mit der Adresse und eine Ganzzahl für die Portnummer enthält: z.B. ('127.0.0.1', 80).

socket

Das Socket-Objekt, auf dem der Server auf eingehende Anfragen lauscht.

Die Serverklassen unterstützen die folgenden Klassenvariablen

allow_reuse_address

Ob der Server die Wiederverwendung einer Adresse zulässt. Dies ist standardmäßig auf False gesetzt und kann in Unterklassen gesetzt werden, um die Richtlinie zu ändern.

request_queue_size

Die Größe der Anfragewarteschlange. Wenn die Verarbeitung einer einzelnen Anfrage lange dauert, werden Anfragen, die ankommen, während der Server beschäftigt ist, bis zu request_queue_size Anfragen in eine Warteschlange gestellt. Sobald die Warteschlange voll ist, erhalten weitere Anfragen von Clients die Fehlermeldung "Connection denied". Der Standardwert ist normalerweise 5, kann aber von Unterklassen überschrieben werden.

socket_type

Der Socket-Typ, der vom Server verwendet wird; socket.SOCK_STREAM und socket.SOCK_DGRAM sind zwei gängige Werte.

timeout

Timeout-Dauer in Sekunden oder None, wenn kein Timeout gewünscht ist. Wenn handle_request() innerhalb des Timeout-Zeitraums keine eingehenden Anfragen erhält, wird die Methode handle_timeout() aufgerufen.

Es gibt verschiedene Server-Methoden, die von Unterklassen von Basis-Serverklassen wie TCPServer überschrieben werden können; diese Methoden sind für externe Benutzer des Serverobjekts nicht nützlich.

finish_request(request, client_address)

Verarbeitet die Anfrage tatsächlich, indem RequestHandlerClass instanziiert und seine handle() Methode aufgerufen wird.

get_request()

Muss eine Anfrage vom Socket akzeptieren und ein 2-Tupel zurückgeben, das das *neue* Socket-Objekt für die Kommunikation mit dem Client und die Adresse des Clients enthält.

handle_error(request, client_address)

Diese Funktion wird aufgerufen, wenn die Methode handle() einer Instanz von RequestHandlerClass eine Ausnahme auslöst. Die Standardaktion ist, den Traceback auf der Standardfehlerausgabe auszugeben und die Verarbeitung weiterer Anfragen fortzusetzen.

Geändert in Version 3.6: Wird jetzt nur noch für Ausnahmen aufgerufen, die von der Klasse Exception abgeleitet sind.

handle_timeout()

Diese Funktion wird aufgerufen, wenn das Attribut timeout auf einen Wert ungleich None gesetzt wurde und die Timeout-Periode abgelaufen ist, ohne dass Anfragen empfangen wurden. Die Standardaktion für Forking-Server besteht darin, den Status beendeter Kindprozesse zu sammeln, während diese Methode bei Threading-Servern nichts tut.

process_request(request, client_address)

Ruft finish_request() auf, um eine Instanz von RequestHandlerClass zu erstellen. Falls gewünscht, kann diese Funktion einen neuen Prozess oder Thread zum Bearbeiten der Anfrage erstellen; die Klassen ForkingMixIn und ThreadingMixIn tun dies.

server_activate()

Wird vom Konstruktor des Servers aufgerufen, um den Server zu aktivieren. Das Standardverhalten für einen TCP-Server ruft einfach listen() auf dem Socket des Servers auf. Kann überschrieben werden.

server_bind()

Wird vom Konstruktor des Servers aufgerufen, um den Socket an die gewünschte Adresse zu binden. Kann überschrieben werden.

verify_request(request, client_address)

Muss einen booleschen Wert zurückgeben; wenn der Wert True ist, wird die Anfrage verarbeitet, und wenn er False ist, wird die Anfrage abgelehnt. Diese Funktion kann überschrieben werden, um Zugriffskontrollen für einen Server zu implementieren. Die Standardimplementierung gibt immer True zurück.

Geändert in Version 3.6: Unterstützung für das Protokoll des Kontextmanagers wurde hinzugefügt. Das Verlassen des Kontextmanagers entspricht dem Aufruf von server_close().

Request Handler Objekte

class socketserver.BaseRequestHandler

Dies ist die Oberklasse aller Request Handler Objekte. Sie definiert die untenstehende Schnittstelle. Eine konkrete Unterklasse von Request Handler muss eine neue Methode handle() definieren und kann jede der anderen Methoden überschreiben. Für jede Anfrage wird eine neue Instanz der Unterklasse erstellt.

setup()

Wird vor der Methode handle() aufgerufen, um alle erforderlichen Initialisierungsaktionen durchzuführen. Die Standardimplementierung tut nichts.

handle()

Diese Funktion muss alle Arbeiten ausführen, die zur Bedienung einer Anfrage erforderlich sind. Die Standardimplementierung tut nichts. Mehrere Instanzattribute sind ihr verfügbar; die Anfrage ist als request verfügbar; die Client-Adresse als client_address; und die Serverinstanz als server, falls sie Zugriff auf serverspezifische Informationen benötigt.

Der Typ von request ist für Datagramm- oder Stream-Dienste unterschiedlich. Für Stream-Dienste ist request ein Socket-Objekt; für Datagramm-Dienste ist request ein Paar aus String und Socket.

finish()

Wird nach der Methode handle() aufgerufen, um alle erforderlichen Aufräumarbeiten durchzuführen. Die Standardimplementierung tut nichts. Wenn setup() eine Ausnahme auslöst, wird diese Funktion nicht aufgerufen.

request

Das *neue* socket.socket Objekt, das zur Kommunikation mit dem Client verwendet wird.

client_address

Client-Adresse, die von BaseServer.get_request() zurückgegeben wird.

server

BaseServer Objekt, das zur Bearbeitung der Anfrage verwendet wird.

class socketserver.StreamRequestHandler
class socketserver.DatagramRequestHandler

Diese Unterklassen von BaseRequestHandler überschreiben die Methoden setup() und finish() und stellen die Attribute rfile und wfile bereit.

rfile

Ein Dateiobjekt, von dem die Anfrage gelesen wird. Unterstützt die lesbare Schnittstelle von io.BufferedIOBase.

wfile

Ein Dateiobjekt, auf das die Antwort geschrieben wird. Unterstützt die schreibbare Schnittstelle von io.BufferedIOBase.

Geändert in Version 3.6: wfile unterstützt nun auch die schreibbare Schnittstelle von io.BufferedIOBase.

Beispiele

Beispiel für socketserver.TCPServer

Dies ist die Serverseite

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    """
    The request handler class for our server.

    It is instantiated once per connection to the server, and must
    override the handle() method to implement communication to the
    client.
    """

    def handle(self):
        # self.request is the TCP socket connected to the client
        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

    # Create the server, binding to localhost on port 9999
    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        # Activate the server; this will keep running until you
        # interrupt the program with Ctrl-C
        server.serve_forever()

Eine alternative Request Handler Klasse, die Streams verwendet (dateiähnliche Objekte, die die Kommunikation vereinfachen, indem sie die Standard-Datei-Schnittstelle bereitstellen)

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.
        # 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())

Der Unterschied besteht darin, dass der Aufruf von readline() im zweiten Handler recv() mehrmals aufrufen wird, bis ein Zeilenumbruchzeichen gefunden wird, während der erste Handler eine recv()-Schleife verwenden musste, um Daten zu sammeln, bis er selbst einen Zeilenumbruch fand. Hätte er nur ein einziges recv() ohne die Schleife verwendet, hätte er nur das bisher vom Client empfangene zurückgegeben. TCP ist Stream-basiert: Daten kommen in der Reihenfolge an, in der sie gesendet wurden, aber es gibt keine Korrelation zwischen den send() oder sendall() Aufrufen des Clients und der Anzahl der recv() Aufrufe auf dem Server, die erforderlich sind, um diese zu empfangen.

Dies ist die Clientseite

import socket
import sys

HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])

# Create a socket (SOCK_STREAM means a TCP socket)
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, "utf-8"))
    sock.sendall(b"\n")

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

print("Sent:    ", data)
print("Received:", received)

Die Ausgabe des Beispiels sollte ungefähr so aussehen

Server

$ python TCPServer.py
127.0.0.1 wrote:
b'hello world with TCP'
127.0.0.1 wrote:
b'python is nice'

Client

$ python TCPClient.py hello world with TCP
Sent:     hello world with TCP
Received: HELLO WORLD WITH TCP
$ python TCPClient.py python is nice
Sent:     python is nice
Received: PYTHON IS NICE

Beispiel für socketserver.UDPServer

Dies ist die Serverseite

import socketserver

class MyUDPHandler(socketserver.BaseRequestHandler):
    """
    This class works similar to the TCP handler class, except that
    self.request consists of a pair of data and client socket, and since
    there is no connection the client address must be given explicitly
    when sending data back via sendto().
    """

    def handle(self):
        data = self.request[0].strip()
        socket = self.request[1]
        print(f"{self.client_address[0]} wrote:")
        print(data)
        socket.sendto(data.upper(), self.client_address)

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    with socketserver.UDPServer((HOST, PORT), MyUDPHandler) as server:
        server.serve_forever()

Dies ist die Clientseite

import socket
import sys

HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])

# SOCK_DGRAM is the socket type to use for UDP sockets
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# As you can see, there is no connect() call; UDP has no connections.
# Instead, data is directly sent to the recipient via sendto().
sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))
received = str(sock.recv(1024), "utf-8")

print("Sent:    ", data)
print("Received:", received)

Die Ausgabe des Beispiels sollte exakt wie beim TCP-Server-Beispiel aussehen.

Asynchrone Mixins

Verwenden Sie die Klassen ThreadingMixIn und ForkingMixIn, um asynchrone Handler zu erstellen.

Ein Beispiel für die Klasse ThreadingMixIn

import socket
import threading
import socketserver

class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):

    def handle(self):
        data = str(self.request.recv(1024), 'ascii')
        cur_thread = threading.current_thread()
        response = bytes("{}: {}".format(cur_thread.name, data), 'ascii')
        self.request.sendall(response)

class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass

def client(ip, port, message):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect((ip, port))
        sock.sendall(bytes(message, 'ascii'))
        response = str(sock.recv(1024), 'ascii')
        print("Received: {}".format(response))

if __name__ == "__main__":
    # Port 0 means to select an arbitrary unused port
    HOST, PORT = "localhost", 0

    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
    with server:
        ip, port = server.server_address

        # Start a thread with the server -- that thread will then start one
        # more thread for each request
        server_thread = threading.Thread(target=server.serve_forever)
        # Exit the server thread when the main thread terminates
        server_thread.daemon = True
        server_thread.start()
        print("Server loop running in thread:", server_thread.name)

        client(ip, port, "Hello World 1")
        client(ip, port, "Hello World 2")
        client(ip, port, "Hello World 3")

        server.shutdown()

Die Ausgabe des Beispiels sollte ungefähr so aussehen

$ python ThreadedTCPServer.py
Server loop running in thread: Thread-1
Received: Thread-2: Hello World 1
Received: Thread-3: Hello World 2
Received: Thread-4: Hello World 3

Die Klasse ForkingMixIn wird auf die gleiche Weise verwendet, außer dass der Server für jede Anfrage einen neuen Prozess starten wird. Nur auf POSIX-Plattformen verfügbar, die fork() unterstützen.