Socket.IO server and client for Python providing real-time bidirectional communication
—
Middleware classes and utilities for integrating Socket.IO servers with popular Python web frameworks. These integration layers enable Socket.IO to work seamlessly with WSGI and ASGI applications while providing static file serving and flexible routing options.
WSGI middleware that integrates a Socket.IO server with WSGI-compatible web frameworks like Flask, Django, and others.
class WSGIApp:
"""
WSGI middleware for Socket.IO applications.
Inherits from: engineio.WSGIApp
Attributes:
socketio_app: Socket.IO server instance
wsgi_app: Wrapped WSGI application
socketio_path (str): Socket.IO endpoint path
"""
def __init__(self, socketio_app, wsgi_app=None, static_files=None, socketio_path='socket.io'):
"""
Initialize the WSGI middleware.
Args:
socketio_app: Socket.IO server instance
wsgi_app: WSGI application to wrap (optional)
static_files (dict): Static file mappings {'/path': 'directory'}
socketio_path (str): Socket.IO endpoint path (default: 'socket.io')
"""
def __call__(self, environ, start_response):
"""
WSGI application callable.
Args:
environ (dict): WSGI environment
start_response (callable): WSGI start_response function
Returns:
WSGI response iterable
"""import socketio
# Create Socket.IO server
sio = socketio.Server(cors_allowed_origins="*")
@sio.event
def connect(sid, environ):
print(f'Client {sid} connected')
@sio.event
def disconnect(sid):
print(f'Client {sid} disconnected')
@sio.event
def message(sid, data):
sio.emit('response', {'echo': data})
# Create WSGI app with static files
app = socketio.WSGIApp(sio, static_files={
'/': 'static/',
'/css': 'static/css/',
'/js': 'static/js/'
})
if __name__ == '__main__':
import eventlet
import eventlet.wsgi
eventlet.wsgi.server(eventlet.listen(('', 5000)), app)import socketio
from flask import Flask
# Create Flask app
flask_app = Flask(__name__)
# Create Socket.IO server
sio = socketio.Server(cors_allowed_origins="*")
@flask_app.route('/')
def index():
return '<h1>Flask + Socket.IO</h1>'
@flask_app.route('/api/data')
def get_data():
return {'message': 'Hello from Flask!'}
@sio.event
def connect(sid, environ):
print(f'Client {sid} connected')
@sio.event
def flask_message(sid, data):
# Can access Flask app context if needed
with flask_app.app_context():
# Flask operations here
pass
sio.emit('flask_response', {'processed': data})
# Wrap Flask app with Socket.IO
app = socketio.WSGIApp(sio, flask_app)
if __name__ == '__main__':
import eventlet
import eventlet.wsgi
eventlet.wsgi.server(eventlet.listen(('', 5000)), app)import socketio
import os
from django.core.wsgi import get_wsgi_application
# Django setup
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
django_app = get_wsgi_application()
# Create Socket.IO server
sio = socketio.Server(cors_allowed_origins="*")
@sio.event
def connect(sid, environ):
print(f'Client {sid} connected')
@sio.event
def django_message(sid, data):
# Can import and use Django models
from myapp.models import Message
message = Message.objects.create(content=data['text'])
sio.emit('message_saved', {'id': message.id})
# Combine Django + Socket.IO
app = socketio.WSGIApp(sio, django_app)import socketio
sio = socketio.Server()
@sio.event
def connect(sid, environ):
print(f'Client connected: {sid}')
# Custom Socket.IO path
app = socketio.WSGIApp(sio, socketio_path='custom-socketio')
# Client would connect to: http://localhost:5000/custom-socketio/ASGI middleware that integrates an AsyncServer with ASGI-compatible frameworks like FastAPI, Starlette, and Django Channels.
class ASGIApp:
"""
ASGI middleware for async Socket.IO applications.
Inherits from: engineio.ASGIApp
Attributes:
socketio_server: AsyncServer instance
other_asgi_app: Wrapped ASGI application
socketio_path (str): Socket.IO endpoint path
"""
def __init__(self, socketio_server, other_asgi_app=None, static_files=None,
socketio_path='socket.io', on_startup=None, on_shutdown=None):
"""
Initialize the ASGI middleware.
Args:
socketio_server: AsyncServer instance
other_asgi_app: ASGI application to wrap (optional)
static_files (dict): Static file mappings {'/path': 'directory'}
socketio_path (str): Socket.IO endpoint path (default: 'socket.io')
on_startup (list): List of startup event handlers
on_shutdown (list): List of shutdown event handlers
"""
async def __call__(self, scope, receive, send):
"""
ASGI application callable.
Args:
scope (dict): ASGI connection scope
receive (callable): ASGI receive function
send (callable): ASGI send function
"""import socketio
# Create async Socket.IO server
sio = socketio.AsyncServer(cors_allowed_origins="*")
@sio.event
async def connect(sid, environ):
print(f'Client {sid} connected')
@sio.event
async def disconnect(sid):
print(f'Client {sid} disconnected')
@sio.event
async def async_message(sid, data):
# Simulate async processing
await asyncio.sleep(0.1)
await sio.emit('async_response', {'processed': data})
# Create ASGI app
app = socketio.ASGIApp(sio, static_files={
'/': 'static/',
})
# Run with: uvicorn main:app --host 0.0.0.0 --port 5000import socketio
from fastapi import FastAPI
# Create FastAPI app
fastapi_app = FastAPI()
# Create async Socket.IO server
sio = socketio.AsyncServer(cors_allowed_origins="*")
@fastapi_app.get("/")
async def read_root():
return {"message": "FastAPI + Socket.IO"}
@fastapi_app.get("/api/users/{user_id}")
async def get_user(user_id: int):
return {"user_id": user_id, "name": f"User {user_id}"}
@sio.event
async def connect(sid, environ):
print(f'Client {sid} connected')
@sio.event
async def fastapi_message(sid, data):
# Can use FastAPI dependencies and features
await sio.emit('fastapi_response', {'echo': data})
# Combine FastAPI + Socket.IO
app = socketio.ASGIApp(sio, fastapi_app)
# Run with: uvicorn main:app --host 0.0.0.0 --port 5000import socketio
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
# Create async Socket.IO server
sio = socketio.AsyncServer(cors_allowed_origins="*")
async def homepage(request):
return JSONResponse({'message': 'Starlette + Socket.IO'})
async def api_endpoint(request):
return JSONResponse({'data': 'API response'})
# Create Starlette app
starlette_app = Starlette(routes=[
Route('/', homepage),
Route('/api/data', api_endpoint),
])
@sio.event
async def connect(sid, environ):
print(f'Client {sid} connected')
@sio.event
async def starlette_message(sid, data):
await sio.emit('starlette_response', {'received': data})
# Combine Starlette + Socket.IO
app = socketio.ASGIApp(sio, starlette_app)import socketio
from fastapi import FastAPI
sio = socketio.AsyncServer()
fastapi_app = FastAPI()
# Startup and shutdown handlers
async def startup_handler():
print("Application starting up...")
# Initialize resources, database connections, etc.
async def shutdown_handler():
print("Application shutting down...")
# Clean up resources
app = socketio.ASGIApp(
sio,
fastapi_app,
on_startup=[startup_handler],
on_shutdown=[shutdown_handler]
)Utility function to create a Tornado request handler for Socket.IO server integration.
def get_tornado_handler(socketio_server):
"""
Get Tornado request handler for Socket.IO server.
Args:
socketio_server: Socket.IO server instance (Server or AsyncServer)
Returns:
Tornado handler class for Socket.IO integration
"""import socketio
import tornado.web
import tornado.ioloop
# Create Socket.IO server
sio = socketio.Server()
@sio.event
def connect(sid, environ):
print(f'Client {sid} connected')
@sio.event
def tornado_message(sid, data):
sio.emit('tornado_response', {'echo': data})
# Create Tornado application
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Tornado + Socket.IO")
# Get Socket.IO handler for Tornado
SocketIOHandler = socketio.get_tornado_handler(sio)
app = tornado.web.Application([
(r"/", MainHandler),
(r"/socket.io/.*", SocketIOHandler),
], debug=True)
if __name__ == "__main__":
app.listen(5000)
print("Server running on http://localhost:5000")
tornado.ioloop.IOLoop.current().start()class Middleware:
"""
Deprecated alias for WSGIApp.
Note:
This class is deprecated. Use WSGIApp instead.
"""The Middleware class is a deprecated alias for WSGIApp. Use WSGIApp directly for new applications.
Both WSGIApp and ASGIApp support static file serving for client-side assets:
# Static file mappings
static_files = {
'/': 'public/', # Serve public/ directory at root
'/css': 'assets/css/', # Serve CSS from assets/css/
'/js': 'assets/js/', # Serve JavaScript from assets/js/
'/images': 'assets/img/' # Serve images from assets/img/
}
# WSGI with static files
wsgi_app = socketio.WSGIApp(sio, flask_app, static_files=static_files)
# ASGI with static files
asgi_app = socketio.ASGIApp(sio, fastapi_app, static_files=static_files)Configure CORS (Cross-Origin Resource Sharing) for web browser clients:
# Allow all origins (development only)
sio = socketio.Server(cors_allowed_origins="*")
# Allow specific origins
sio = socketio.Server(cors_allowed_origins=[
"http://localhost:3000",
"https://myapp.com"
])
# Async server CORS
async_sio = socketio.AsyncServer(cors_allowed_origins=[
"http://localhost:8080",
"https://app.example.com"
])Change the default Socket.IO endpoint path:
# Custom endpoint path
app = socketio.WSGIApp(sio, socketio_path='realtime')
# Clients connect to: /realtime/
app = socketio.ASGIApp(sio, socketio_path='ws')
# Clients connect to: /ws/# With Gunicorn + eventlet
# gunicorn --worker-class eventlet -w 1 main:app
# With uWSGI + gevent
# uwsgi --http :5000 --gevent 1000 --module main:app# With Uvicorn
# uvicorn main:app --host 0.0.0.0 --port 5000
# With Hypercorn
# hypercorn main:app --bind 0.0.0.0:5000
# With Daphne (Django Channels)
# daphne -b 0.0.0.0 -p 5000 main:appInstall with Tessl CLI
npx tessl i tessl/pypi-python-socketio