Développement de clients WebSocket avec Python

Le protocole WebSocket permet d'établir un canal de communication bidirectionnel et persistant sur une seule connexion TCP. Contrairement au modèle HTTP classique basé sur des requêtes-réponses, les WebSockets autorisent le serveur à pousser des données vers le client de manière proactive, ce qui est idéal pour les applications temps réel.

Caractéritsiques principales du protocole

  • Communication Full-Duplex : Échange simultané de données entre l'émetteur et le récepteur.
  • Persistance : Une fois la poignée de main (handshake) effectuée, la connexion reste ouverte.
  • Légèreté : Réduction de l'en-tête des messages par rapport à HTTP, minimisant la latence globale.

Sélection d'une bibliothèque Python

Plusieurs options s'offrent aux développeurs Python pour interagir avec des serveurs WebSocket :

  • websockets : Une bibliothèque moderne construite sur asyncio, privilégiée pour sa simplicité et ses performances.
  • websocket-client : Une implémentation synchrone plus traditionnelle.
  • autobahn : Un framework complet supportant WebSocket et WAMP.

L'installation de la bibliothèque recommandée se fait via pip :

pip install websockets

Implémentation d'un client asynchrone de base

L'exemple suivant utilise la syntaxe async/await pour établir une connexion, transmettre une chaîne de caractères et traiter la réponse du serveur.

import asyncio
import websockets

async def demarrer_client_ws():
    url_serveur = "ws://localhost:8000"
    
    async with websockets.connect(url_serveur) as session:
        print(f"Connexion établie avec {url_serveur}")

        # Envoi d'une charge utile
        donnees_sortantes = "Action: Ping"
        await session.send(donnees_sortantes)
        print(f"Envoi : {donnees_sortantes}")

        # Attente de la réponse
        reponse = await session.recv()
        print(f"Réponse reçue : {reponse}")

if __name__ == "__main__":
    asyncio.run(demarrer_client_ws())

Cas d'utilisation : Messagerie interactive

Dans ce scénario, le client maintient une boucle active pour permettre l'envoi continu de messages utilisateur tout en écoutant les retours du serveur.

import asyncio
import websockets

async def flux_messagerie():
    cible = "ws://localhost:8000/chat"
    try:
        async with websockets.connect(cible) as ws:
            print("Session de chat active. Tapez 'exit' pour quitter.")
            
            while True:
                texte = input("Votre message > ")
                if texte.lower() == 'exit':
                    break
                
                await ws.send(texte)
                echo = await ws.recv()
                print(f"[Serveur] : {echo}")
    except websockets.exceptions.ConnectionClosed:
        print("La connexion a été interrompue.")

asyncio.run(flux_messagerie())

Cas d'utilisation : Réception de données boursières

Ce modèle se concentre sur la consommation d'un flux de données descendant (streaming) où le client traite les mises à jour envoyées par le serveur de manière asynchrone.

import asyncio
import websockets
import json

async def monitoring_marche():
    uri_api = "wss://api.bourse-exemple.com/ticks"
    
    async with websockets.connect(uri_api) as flux:
        print("Abonnement au flux boursier...")
        
        while True:
            try:
                raw_data = await flux.recv()
                data_parsed = json.loads(raw_data)
                symbole = data_parsed.get("symbol")
                prix = data_parsed.get("price")
                print(f"Mise à jour : {symbole} -> {prix} USD")
            except Exception as e:
                print(f"Erreur de flux : {e}")
                break

asyncio.run(monitoring_marche())

Cas d'utilisation : Visualisation de métriques en temps réel

L'intégration avec des bibliothèques de visualisation comme Matplotlib permet de transformer les flux WebSocket en graphiques dynamiques.

import asyncio
import websockets
import matplotlib.pyplot as plt

# Configuration de l'affichage dynamique
plt.style.use('ggplot')
valeurs_y = []
indices_x = []

async def recuperer_et_tracer():
    endpoint = "ws://votre-serveur-metriques.io/stats"
    
    async with websockets.connect(endpoint) as canal:
        plt.ion()
        fig, axe = plt.subplots()
        courbe, = axe.plot(indices_x, valeurs_y, color='green')

        while True:
            msg = await canal.recv()
            nombre = float(msg)
            
            valeurs_y.append(nombre)
            indices_x.append(len(valeurs_y))
            
            # Limitation de la fenêtre d'affichage (20 derniers points)
            if len(valeurs_y) > 20:
                indices_x.pop(0)
                valeurs_y.pop(0)

            courbe.set_xdata(range(len(valeurs_y)))
            courbe.set_ydata(valeurs_y)
            axe.relim()
            axe.autoscale_view()
            
            plt.pause(0.1)
            plt.draw()

asyncio.run(recuperer_et_tracer())

Le choix d'une architecture asynchrone via asyncio est crucial pour les clients WebSocket, car il permet de gérer efficacement les entrées/sorties sans bloquer l'exécution globale du programme, notamment lors de l'attente de paquets réseau.

Étiquettes: Python WebSocket asyncio Network-Programming real-time

Publié le 23 juillet à 20h46