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.
- 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)
- 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)
- 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())
- 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())