Introduction

django-sio lets a Django project speak Socket.IO while still using Django Channels for ASGI routing, HTTP handling, WebSocket handling, and channel-layer broadcasts.

You write application code against Socket.IO concepts:

  • namespaces such as "/" and "/chat";

  • rooms such as "lobby";

  • events such as "chat_message" and "live_state";

  • acknowledgements, often called acks, where the client passes a callback and the server calls it after processing an event.

Everything below that application layer is handled for you:

  • HTTP versus WebSocket transport selection;

  • Engine.IO ping/pong and heartbeats;

  • HTTP long-polling to WebSocket upgrade;

  • Engine.IO packet framing;

  • Socket.IO packet parsing;

  • room broadcasting through the Channels channel layer.

Mental model

If you are used to Node.js Socket.IO, you can think of this package as:

Your Django code ↔ Socket.IO ↔ Engine.IO ↔ raw HTTP/WebSocket

You stay on the left-hand side. You create a consumer, define lifecycle hooks, write event handlers, join rooms, leave rooms, and emit events.

A single client connection has two layers of identity:

  • an Engine.IO session, which owns the transport connection and heartbeat state;

  • one or more Socket.IO namespace sockets, which represent the application-level Socket.IO connection for each namespace.

For most application code you only interact with the namespace socket passed to your consumer methods.

Where Channels fits

Django Channels is responsible for accepting ASGI scopes and dispatching them to consumers. django-sio exposes one ASGI application through SocketIOConsumer.as_asgi() and that application handles both:

  • HTTP scopes, used by the Engine.IO long-polling transport; and

  • WebSocket scopes, used by the Engine.IO WebSocket transport.

The same Socket.IO endpoint must therefore be routed for both HTTP and WebSocket traffic.

What you normally write

A typical application has one or more subclasses of sio.SocketIOConsumer. Each subclass declares a namespace and methods for connection lifecycle events and Socket.IO events.

from sio import SocketIOConsumer

class ChatConsumer(SocketIOConsumer):
    namespace = "/chat"

    async def connect(self, socket, auth):
        await socket.join("lobby")
        await socket.emit("system_message", {"text": "Welcome!"})
        return True

    async def disconnect(self, socket, reason):
        pass

    async def event_chat_message(self, socket, payload, ack=None):
        await socket.server.emit(
            "chat_message",
            payload,
            room="lobby",
            namespace=socket.namespace,
        )

        if ack is not None:
            await ack({"status": "ok"})

Any method named event_<name> handles the Socket.IO event named "<name>" for that consumer’s namespace.

What this project is not

django-sio does not replace Django Channels. It builds on Channels.

It also does not turn the Channels channel layer into shared Engine.IO session storage. The current Engine.IO session registry is in-process, so deployments with multiple workers need sticky sessions. See Deployment and scaling for the full production caveat.