Les sockets et la programmation réseau¶
Le module socket
permet de programmer des accès réseau bas niveau en Python.
Dans ce chapitre, nous présenterons uniquement l’utilisation des sockets TCP/IP
mais le module socket
permet, en utilisant la même API, de créer et
d’utiliser des sockets Unix, des sockets Bluetooth…
L’API socket suppose une architecture client/serveur. Le serveur est en attente d’une connexion d’un client. Des données peuvent être lues et écrites aussi bien par le client que par le serveur.
Exemple d’implémentation d’un serveur¶
Pour implémenter un serveur, il suffit d’appeler la fonction socket()
en spécifiant le type. Les constantes socket.AF_INET
et socket.SOCK_STREAM
passées en paramètres permettent de spécifier respectivement que l’on veut
créer une socket IP pour une transmission par paquets, c’est-à-dire une socket TCP.
Une socket doit être fermée après usage. À la ligne 6, la socket est crée dans
une instruction with
pour s’assurer qu’elle sera fermée à la fin du bloc.
On appelle les méthodes suivantes :
setsockopt()
Pour spécifier les paramètres pour la socket. Ici, on passe 1 pour la valeur
socket.SO_REUSEADDR
afin d’indiquer au système que le port utilisé par la socket peut être immédiatement réutilisé dès que cette dernière est fermée. Sinon, le système temporise la réutilisation et la réexécution du programme peut entraîner un échec.bind()
Pour spécifier sous la forme d’un n-uplet le nom ou l’adresse de l’hôte et le port utilisés par cette socket.
listen()
Pour spécifier qu’il s’agit d’une socket serveur et le nombre de connexions en attente qui peuvent être tolérées avant rejet par le système.
accept()
Cet appel suspend l’exécution du thread jusqu’à ce qu’une connexion entrante se produise pour cette socket. Alors, le programme est réveillé et cette méthode retourne une socket de communication avec le client ainsi que l’adresse du client.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import socket
host = 'localhost'
port = 8000
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
s.listen(1)
while True:
conn, address = s.accept()
with conn:
buff = conn.recv(512)
message = buff.decode('utf-8')
conn.sendall(f"echo : {message}".encode('utf-8'))
|
À partir de la socket de connexion du client, il est possible de lire les données reçues
grâce à la méthode recv()
et de répondre avec la méthode
send()
. Les données lues et écrites par une socket sont
des objets de type bytes
.
Exemple d’implémentation d’un client¶
On utilise la même fonction socket()
pour créer une socket cliente.
Pour le client, on appelle la méthode :
connect()
Pour se connecter au serveur en indiquant son adresse et son port.
1 2 3 4 5 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))
s.sendall("Hello world".encode('utf-8'))
buff = s.recv(512)
print(buff.decode())
|
À partir de la socket client, il est possible d’envoyer des données avec la méthode
send()
et de lire la réponse du serveur grâce à la méthode
recv()
. Les données lues et écrites par une socket sont
des objets de type bytes
.
Exemple d’un serveur multi-thread¶
Il est possible d’améliorer l’implémentation du serveur en créant des worker threads
pour prendre en charge la connexion de chaque client. Il est ainsi possible
pour le serveur de traiter simultanément avec plusieurs clients. On peut
utiliser le module multiprocessing
et sa classe Pool
qui gère en groupe de threads réutilisables :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import socket, multiprocessing
host = 'localhost'
port = 8000
nb_workers = 10
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
s.listen(1)
with multiprocessing.Pool(nb_workers) as pool:
while True:
conn, address = s.accept()
pool.apply(handle, (conn, address))
def handle(conn, address):
with conn:
buff = conn.recv(512)
message = buff.decode('utf-8')
conn.sendall(f"echo : {message}".encode('utf-8'))
|