HOWTO · Python

Socket-Programmierung in Python: Ein Leitfaden für Anfänger

Dieser Artikel behandelt die Socket-Programmierung in Python. Wir haben die Kommunikation unter Verwendung des TCP-Protokolls und des UDP-Protokolls unter Verwendung der Socket-Programmierung in Python besprochen.

Auf dieser Seite

Mit der Socket-Programmierung in Python können zwei Prozesse über Kommunikationsendpunkte, sogenannte Sockets, Daten austauschen. Dieser Leitfaden verwendet das socket-Modul aus der Standardbibliothek, um lokale Client-Server-Kommunikation mit TCP und UDP aufzubauen, und erläutert, wann sich welches Protokoll eignet. Eine zusätzliche Paketinstallation ist nicht erforderlich.

Socket-Programmierung in Python: Überblick und Einrichtung

Ein Server wartet auf einen Client; ein Client initiiert die Kommunikation mit dem Server. In diesen Beispielen laufen beide auf demselben Computer und verwenden 127.0.0.1 sowie denselben Port. Der Konstruktor socket.socket() erstellt einen Socket. Wählen Sie socket.SOCK_STREAM für TCP oder socket.SOCK_DGRAM für UDP. Text muss vor dem Senden in Bytes codiert und nach dem Empfang decodiert werden.

Führen Sie jeden vollständigen Codeblock unten einmal in einem einzigen Terminal aus. Jedes Skript startet seinen Server in einem Hintergrund-Thread. Anschließend übernimmt dasselbe Skript die Rolle des Clients, tauscht über die Loopback-Schnittstelle eine Nachricht aus und schließt die Sockets.

TCP-Server und -Client erstellen

TCP stellt einen verbindungsorientierten, zuverlässigen und geordneten Bytestrom bereit. Der Server bindet sich an eine Adresse und einen Port, ruft listen() auf und wartet in accept(). Der Client verbindet sich mit derselben Adresse und demselben Port. Sobald die Verbindung besteht, können beide Seiten Bytes senden und empfangen. sendall() sendet alle Bytes des übergebenen Puffers, während recv(1024) bis zu 1.024 Bytes liest. Ein TCP-Lesevorgang kennzeichnet keine Grenzen zwischen Anwendungsnachrichten. Ein größeres Protokoll benötigt daher eine eigene Rahmung, beispielsweise durch ein Längenpräfix oder ein Trennzeichen.

Führen Sie dieses Beispiel aus:

Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.

import socket
import threading

HOST = "127.0.0.1"
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe:
    probe.bind((HOST, 0))
    PORT = probe.getsockname()[1]
ready = threading.Event()


def serve():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind((HOST, PORT))
        server.listen(1)
        ready.set()
        connection, _ = server.accept()
        with connection:
            message = connection.recv(1024).decode("utf-8")
            connection.sendall(("Received: " + message).encode("utf-8"))


thread = threading.Thread(target=serve)
thread.start()
ready.wait()
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client:
    client.connect((HOST, PORT))
    client.sendall(b"Hello over TCP")
    reply = client.recv(1024).decode("utf-8")
thread.join()
print(reply)

Das Betriebssystem weist einen verfügbaren lokalen Port zu, sodass das Beispiel ohne Konflikt mit einem festgelegten Port ausgeführt werden kann. Durch den Thread kann der Server Verbindungen annehmen, während sich der Client verbindet. Der jeweilige Kontextmanager schließt jeden Socket nach dem Austausch.

UDP-Server und -Client erstellen

UDP sendet einzelne Datagramme, ohne eine Verbindung wie bei TCP aufzubauen. Der Server bindet sich an eine Adresse und wartet mit recvfrom() auf Daten. Dieser Aufruf gibt sowohl die empfangenen Bytes als auch die Adresse des Absenders zurück. Der Client verwendet sendto() mit der Serveradresse und anschließend recvfrom(), um die Antwort samt Absenderadresse zu empfangen. Das folgende lokale Beispiel verwendet dieselbe Loopback-Adresse und lässt das Betriebssystem den Serverport auswählen.

Führen Sie dieses Beispiel aus:

Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.

import socket
import threading

HOST = "127.0.0.1"
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as probe:
    probe.bind((HOST, 0))
    PORT = probe.getsockname()[1]
ready = threading.Event()


def serve():
    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as server:
        server.bind((HOST, PORT))
        ready.set()
        message, client_address = server.recvfrom(1024)
        reply = ("Received: " + message.decode("utf-8")).encode("utf-8")
        server.sendto(reply, client_address)


thread = threading.Thread(target=serve)
thread.start()
ready.wait()
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as client:
    client.sendto(b"Hello over UDP", (HOST, PORT))
    reply, server_address = client.recvfrom(1024)
thread.join()
print(reply.decode("utf-8"))

Ein Datagramm wird an eine Zieladresse und einen Zielport gesendet. UDP garantiert jedoch weder, dass es ankommt, noch dass mehrere Datagramme in der richtigen Reihenfolge eintreffen. Im Beispiel werden die Nachrichtenbytes vor dem Erstellen der Antwort decodiert. So enthält die Antwort lesbaren Text statt der Python-Darstellung eines Bytes-Objekts.

TCP oder UDP auswählen

Wählen Sie TCP, wenn die Anwendung einen geordneten Bytestrom und das Zustellungs- und Wiederholungsverhalten des Transports benötigt. Anwendungen müssen dennoch Verbindungsfehler behandeln und die Rahmung ihrer Nachrichten festlegen; TCP garantiert nicht, dass die Anwendung auf der Gegenseite eine Nachricht verarbeitet. Wählen Sie UDP, wenn einzelne Datagramme zur Aufgabe passen und die Anwendung Verluste oder eine geänderte Reihenfolge tolerieren oder selbst behandeln kann. UDP ist nicht grundsätzlich schneller: Die Leistung hängt vom Netzwerk, der Arbeitslast und den benötigten Zuverlässigkeitsfunktionen der Anwendung ab. Mit UDP ist Broadcast möglich, sofern Socket- und Netzwerkeinstellungen dies erlauben. Es ist jedoch kein allgemeiner Ersatz für einen zuverlässigen Client-Server-Austausch.

Fazit

Das socket-Modul stellt die Bausteine für beide Protokolle bereit. Die TCP-Beispiele verwenden SOCK_STREAM, accept(), sendall() und recv(), um Daten über eine Verbindung auszutauschen. Die UDP-Beispiele nutzen SOCK_DGRAM, sendto() und recvfrom(), um adressierte Datagramme auszutauschen. Beginnen Sie mit einem lokalen Client und Server und wählen Sie anschließend TCP oder UDP entsprechend dem Zustellungsverhalten, das Ihre Anwendung benötigt.