Implémentation des WebSockets en Python avec Socket.IO, Flask, Tornado et WebSockets

Communication en Temps Réel en Python via les WebSockets

Les WebSockets établissent un canal de communication bidirectionnel persistant entre le client et le serveur. Ils offrent une transmission de données à faible latence et à surcharge réduite par rapport aux requêtes HTTP classiques, idéales pour les applications nécessitant des mises à jour instantanées.

  1. Bibilothèque python-socketio

Installation


pip install python-socketio websocket-client

Exemple de Client


import socketio

# Initialisation du client avec journalisation activée
client_instance = socketio.Client(logger=True)
endpoint = "/notifications"

# Gestionnaire pour l'événement de connexion
@client_instance.on('connect', namespace=endpoint)
def on_server_connect():
    print('Établissement de la connexion réussie.')

# Gestionnaire pour la réception de données
@client_instance.on('update_received', namespace=endpoint)
def handle_update(payload):
    print(f'Donnée mise à jour : {payload}')

if __name__ == '__main__':
    client_instance.connect('http://localhost:8080', transports=['websocket'])
    client_instance.emit('request_data', {'client_id': 'web_app'}, namespace=endpoint)
    client_instance.wait()

Exemple de Serveur


import socketio
from flask import Flask

# Serveur Socket.IO configuré pour le multithreading
server_sio = socketio.Server(async_mode='threading')
application = Flask(__name__)
application.wsgi_app = socketio.WSGIApp(server_sio, application.wsgi_app)

endpoint = "/notifications"

@server_sio.event(namespace=endpoint)
def connect(sid, environ):
    print(f'Connexion établie avec le client : {sid}')

@server_sio.event(namespace=endpoint)
def disconnect(sid):
    print(f'Déconnexion du client : {sid}')

@server_sio.event(namespace=endpoint)
def request_data(sid, payload):
    print(f'Demande reçue de {sid} : {payload}')
    # Envoi d'une mise à jour à tous les clients abonnés
    server_sio.emit('update_received', {'status': 'active', 'data': [1, 2, 3]}, namespace=endpoint)

if __name__ == '__main__':
    application.run(host='0.0.0.0', port=8080, debug=True)
  1. Intégration avec Flask-SocketIO

Dépendances Requises


pip install flask-socketio eventlet

Client Web (HTML/JavaScript)


<html>
<head>
    <title>Client Flask-SocketIO</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
</head>
<body>
    <div id="messages"></div>
    <input type="text" id="inputMessage" placeholder="Entrez un message">
    <button onclick="sendMessage()">Envoyer</button>
    <script>
        const socket = io('http://localhost:8080/chat');
        socket.on('connect', () => {
            console.log('Connecté au canal de discussion');
        });
        socket.on('message_broadcast', (data) => {
            document.getElementById('messages').innerHTML += `<p>${data.user}: ${data.text}</p>`;
        });
        function sendMessage() {
            const input = document.getElementById('inputMessage');
            socket.emit('send_chat', { user: 'Utilisateur', text: input.value });
            input.value = '';
        }
    </script>
</body>
</html>

Serveur Flask


from threading import Lock
from flask import Flask, render_template
from flask_socketio import SocketIO, emit, join_room

app = Flask(__name__)
app.config['SECRET_KEY'] = 'cle_secrete!'
socket_io = SocketIO(app, async_mode='eventlet', cors_allowed_origins="*")

chat_namespace = '/chat'
thread_lock = Lock()

# Classe pour gérer les événements du namespace
class ChatNamespace:
    def on_connect(self):
        print('Nouveau client connecté au chat')
        emit('connection_ack', {'status': 'success'})

    def on_send_chat(self, data):
        # Diffuser le message à tous les clients du namespace
        emit('message_broadcast', data, broadcast=True)

    def on_join_room(self, data):
        room = data.get('room')
        join_room(room)
        emit('room_notification', {'message': f"Rejoint la salle {room}"}, room=room)

socket_io.on_namespace(ChatNamespace(chat_namespace))

@app.route('/')
def index():
    return render_template('chat_client.html')

if __name__ == '__main__':
    socket_io.run(app, host='0.0.0.0', port=8080)
  1. Utilisation avec Tornado

Installation


pip install tornado

Client Minimal


<html>
<head>
    <title>WebSocket Tornado</title>
</head>
<body>
    <ul id="log"></ul>
    <input type="text" id="dataInput">
    <button id="sendBtn">Envoyer</button>
    <script>
        const ws = new WebSocket('ws://localhost:8080/ws');
        ws.onopen = () => console.log('Connexion WebSocket établie');
        ws.onmessage = (event) => {
            const li = document.createElement('li');
            li.textContent = event.data;
            document.getElementById('log').appendChild(li);
        };
        document.getElementById('sendBtn').onclick = () => {
            ws.send(document.getElementById('dataInput').value);
        };
    </script>
</body>
</html>

Serveur Tornado


import asyncio
import tornado.web
import tornado.websocket

# Gestionnaire pour les requêtes HTTP
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("tornado_client.html")

# Gestionnaire pour les connexions WebSocket
class DataStreamHandler(tornado.websocket.WebSocketHandler):
    clients = set()

    def open(self):
        self.clients.add(self)
        print(f'Client WebSocket ajouté. Total : {len(self.clients)}')
        for client in self.clients:
            client.write_message('Nouveau participant connecté.')

    def on_message(self, message):
        print(f'Message reçu : {message}')
        for client in self.clients:
            client.write_message(f'Diffusion : {message}')

    def on_close(self):
        self.clients.remove(self)
        print(f'Client WebSocket retiré. Total : {len(self.clients)}')

async def start_server():
    app = tornado.web.Application([
        (r'/', MainHandler),
        (r'/ws', DataStreamHandler),
    ], template_path='templates', static_path='static')
    app.listen(8080)
    await asyncio.Event().wait()

if __name__ == '__main__':
    asyncio.run(start_server())
  1. Approche Native avec la Bibliothèque websockets

Installation


pip install websockets

Client Asynchrone


import asyncio
import websockets

async def websocket_client():
    uri = "ws://localhost:8080"
    async with websockets.connect(uri) as connection:
        await connection.send("Demande d'initialisation")
        response = await connection.recv()
        print(f"Réponse du serveur : {response}")

if __name__ == '__main__':
    asyncio.run(websocket_client())

Serveur Asynchrone


import asyncio
from websockets.server import serve

async def handle_connection(websocket):
    print("Nouvelle connexion établie")
    async for message in websocket:
        print(f"Message reçu : {message}")
        processed = message.upper()
        await websocket.send(f"Traité : {processed}")

async def main():
    async with serve(handle_connection, "0.0.0.0", 8080):
        await asyncio.Future()  # Garder le serveur actif indéfiniment

if __name__ == '__main__':
    asyncio.run(main())

Étiquettes: Python WebSocket socketio flask-socketio tornado

Publié le 16 juin à 04h33