HOWTO · Python
Programmation de sockets en Python: un guide pour débutants
Cet article traite de la programmation des sockets en Python. Nous avons discuté de la communication à l'aide du protocole TCP et du protocole UDP à l'aide de la programmation par socket en Python.
Sur cette page
La programmation de sockets en Python permet à deux processus d’échanger des données par l’intermédiaire de points de communication appelés sockets. Ce guide utilise le module socket de la bibliothèque standard pour créer des échanges locaux entre client et serveur avec TCP et UDP, puis explique dans quels cas chaque protocole est utile. Aucun paquet n’a besoin d’être installé.
Présentation et configuration de la programmation de sockets en Python
Un serveur attend un client ; un client amorce la communication avec le serveur. Dans ces exemples, les deux s’exécutent sur un même ordinateur, en utilisant 127.0.0.1 et le même port. Le constructeur socket.socket() crée un socket. Choisissez socket.SOCK_STREAM pour TCP ou socket.SOCK_DGRAM pour UDP. Le texte doit être encodé en octets avant son envoi, puis décodé après sa réception.
Exécutez une fois chaque bloc de code complet ci-dessous dans un seul terminal. Chaque script démarre son serveur dans un thread d’arrière-plan, puis le même script joue le rôle du client pour échanger un message via l’interface de bouclage et fermer ses sockets.
Créer un serveur et un client TCP
TCP utilise un flux d’octets orienté connexion, fiable et ordonné. Le serveur associe le socket à une adresse et à un port, appelle listen() et attend dans accept(). Le client se connecte à cette même adresse et à ce même port. Une fois la connexion établie, les deux côtés peuvent envoyer et recevoir des octets. sendall() envoie tous les octets du tampon fourni, tandis que recv(1024) lit jusqu’à 1 024 octets. Une lecture TCP ne marque pas les limites des messages de l’application : un protocole plus élaboré doit donc définir son propre cadrage, par exemple au moyen d’un préfixe de longueur ou d’un délimiteur.
Exécutez cet exemple :
Le code et les libellés ou sorties affichés dans cette section restent en anglais.
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)
Le système d’exploitation attribue un port local disponible, ce qui permet d’exécuter l’exemple sans conflit avec un port fixe. Le thread permet au serveur d’attendre la connexion pendant que le client se connecte ; le gestionnaire de contexte de chaque socket le ferme après l’échange.
Créer un serveur et un client UDP
UDP envoie des datagrammes indépendants sans établir de connexion comme le ferait TCP. Le serveur associe le socket à une adresse et attend avec recvfrom(), qui renvoie à la fois les octets reçus et l’adresse de l’expéditeur. Le client utilise sendto() avec l’adresse du serveur, puis recvfrom() pour recevoir la réponse et l’adresse de son expéditeur. L’exemple local ci-dessous utilise la même adresse de bouclage et laisse le système d’exploitation choisir le port du serveur.
Exécutez cet exemple :
Le code et les libellés ou sorties affichés dans cette section restent en anglais.
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"))
Un datagramme est envoyé à une adresse et à un port de destination, mais UDP ne garantit ni son arrivée ni l’ordre d’arrivée de plusieurs datagrammes. L’exemple décode les octets du message avant de construire la réponse : celle-ci contient donc du texte lisible plutôt que la représentation Python d’un objet bytes.
Choisir entre TCP et UDP
Choisissez TCP lorsque l’application a besoin d’un flux d’octets ordonné et des mécanismes de livraison et de retransmission du transport. L’application doit tout de même gérer les échecs de connexion et définir le cadrage des messages ; TCP ne garantit pas que l’application distante traite un message. Choisissez UDP lorsque des datagrammes indépendants conviennent à la tâche et que l’application peut tolérer les pertes ou le désordre, ou les gérer elle-même. UDP n’est pas intrinsèquement plus rapide : les performances dépendent du réseau, de la charge de travail et des garanties de fiabilité nécessaires à l’application. La diffusion est possible avec UDP lorsque les paramètres du socket et du réseau le permettent, mais elle ne remplace pas de manière générale un échange fiable entre client et serveur.
Conclusion
Le module socket fournit les éléments de base pour les deux protocoles. Les exemples TCP utilisent SOCK_STREAM, accept(), sendall() et recv() pour échanger des données sur une connexion ; les exemples UDP utilisent SOCK_DGRAM, sendto() et recvfrom() pour échanger des datagrammes adressés. Commencez par un client et un serveur locaux, puis choisissez TCP ou UDP selon le comportement de livraison dont votre application a besoin.