Of course! Here's a comprehensive guide to using WebSockets in Python, covering the fundamentals, popular libraries, and a complete practical example.

What are WebSockets?
Imagine a standard HTTP request-response like a phone call:
- You (client) call the server.
- You talk (send a request).
- The server listens and then hangs up.
- The server calls you back (sends a response).
- You hang up.
This is inefficient for things that need constant, two-way communication like chat apps, live notifications, or real-time dashboards.
WebSockets are like a walkie-talkie:
- You (client) and the server establish a single, long-lived connection.
- Either party can "talk" (send a message) at any time without waiting for a response.
- The connection stays open until it's explicitly closed.
This makes WebSockets perfect for:

- Real-time chat applications
- Live notifications
- Multiplayer online games
- Real-time data dashboards (stock prices, sensor data)
- Collaborative editing tools (like Google Docs)
Key Python Libraries for WebSockets
You'll generally need two libraries:
- A Server Library: To create the WebSocket server that listens for connections.
- A Client Library: To create a client that connects to a WebSocket server.
Here are the most popular choices:
| Library | Server | Client | Description |
|---|---|---|---|
websockets |
✅ | ✅ | The de-facto standard library. Pure Python, powerful, and easy to use. Great for learning and most applications. |
Socket.IO |
✅ | ✅ | Built on top of WebSockets. Provides fallbacks (like long-polling), rooms, namespaces, and automatic reconnection. More feature-rich. |
Django Channels |
✅ | ❌ | Integrates WebSockets directly into the Django framework. The go-to choice if you're already using Django. |
FastAPI |
✅ | ❌ | Modern, fast web framework with built-in support for WebSockets via Starlette. Excellent for building APIs with real-time features. |
For this guide, we'll focus on the websockets library as it's the most fundamental and widely used.
Step-by-Step Example: A Simple Chat App
We'll build a simple echo server and a client using the websockets library.

Step 1: Installation
First, you need to install the library. Open your terminal or command prompt and run:
pip install websockets
Step 2: The WebSocket Server
Create a file named server.py. This server will listen for incoming WebSocket connections. When a client connects, it will wait for a message, print it, and then send the same message back to the client (an "echo" server).
# server.py
import asyncio
import websockets
# This function will be called for each new client connection
async def handler(websocket, path):
"""
Handle a WebSocket connection.
`websocket`: The connection object for the client.
`path`: The path the client connected to.
"""
print(f"A new client connected from {websocket.remote_address}")
try:
# Keep listening for messages from the client indefinitely
async for message in websocket:
print(f"Received message: {message}")
# Send the same message back to the client
await websocket.send(f"Echo: {message}")
except websockets.exceptions.ConnectionClosed as e:
print(f"Connection closed: {e}")
finally:
print("Client disconnected.")
# Start the WebSocket server
async def main():
# host="0.0.0.0" makes the server accessible from other machines on the network
# port=8765 is the port to listen on
async with websockets.serve(handler, "0.0.0.0", 8765):
print("WebSocket server started on ws://0.0.0.0:8765")
# Keep the server running forever
await asyncio.Future() # run forever
# Run the main function
if __name__ == "__main__":
asyncio.run(main())
To run the server:
python server.py
You should see the output: WebSocket server started on ws://0.0.0.0:8765
Step 3: The WebSocket Client
Now, create a file named client.py. This client will connect to the server we just created, send a message, and then print the response it receives.
# client.py
import asyncio
import websockets
async def client():
uri = "ws://localhost:8765" # The server's URI
try:
# Connect to the WebSocket server
async with websockets.connect(uri) as websocket:
print("Connected to the server!")
# Send a message to the server
message = "Hello, Server!"
await websocket.send(message)
print(f"Sent: {message}")
# Wait for a response from the server
response = await websocket.recv()
print(f"Received: {response}")
except websockets.exceptions.ConnectionClosed:
print("Connection to the server was closed.")
except ConnectionRefusedError:
print("Could not connect to the server. Is it running?")
except Exception as e:
print(f"An error occurred: {e}")
# Run the client
if __name__ == "__main__":
asyncio.run(client())
To run the client: Open a new terminal window and run:
python client.py
Expected Output:
In the Server Terminal:
A new client connected from 127.0.0.1:54321
Received message: Hello, Server!
Client disconnected.
In the Client Terminal:
Connected to the server!
Sent: Hello, Server!
Received: Echo: Hello, Server!
Advanced Concepts: Broadcasting to Multiple Clients
A real chat app needs to send a message from one client to all other clients. This is called "broadcasting". The websockets library makes this easy with a set to keep track of all connected clients.
Step 1: Modify the Server for Broadcasting
Update your server.py to handle multiple clients and broadcast messages.
# server.py (Broadcasting Version)
import asyncio
import websockets
import json
# A set to keep track of all connected clients
connected_clients = set()
async def handler(websocket, path):
"""
Handle a WebSocket connection, add the client to the set,
and broadcast messages from this client to all others.
"""
# Add the new client to our set
connected_clients.add(websocket)
client_address = websocket.remote_address
print(f"Client connected: {client_address}. Total clients: {len(connected_clients)}")
try:
async for message in websocket:
print(f"Received from {client_address}: {message}")
# Broadcast the message to all other clients
await broadcast(message, exclude=websocket)
except websockets.exceptions.ConnectionClosed as e:
print(f"Connection closed for {client_address}: {e}")
finally:
# Remove the client from the set when they disconnect
connected_clients.remove(websocket)
print(f"Client disconnected: {client_address}. Total clients: {len(connected_clients)}")
async def broadcast(message, exclude=None):
"""
Send a message to all connected clients, except for the one specified.
"""
# Create a list of tasks to send the message to each client
tasks = [
client.send(message)
for client in connected_clients
if client is not exclude # Don't send back to the original sender
]
# Wait for all the send operations to complete
if tasks:
await asyncio.gather(*tasks, return_exceptions=True)
async def main():
async with websockets.serve(handler, "0.0.0.0", 8765):
print("Broadcasting WebSocket server started on ws://0.0.0.0:8765")
await asyncio.Future() # run forever
if __name__ == "__main__":
asyncio.run(main())
Step 2: Create a More Interactive Client
Now, let's create a client that can continuously send messages. This is more like a real chat application.
# client.py (Interactive Version)
import asyncio
import websockets
import json
async def client():
uri = "ws://localhost:8765"
username = input("Enter your username: ")
try:
async with websockets.connect(uri) as websocket:
print(f"Connected to the server as {username}!")
# Create a task to listen for messages from the server
receive_task = asyncio.create_task(receive_messages(websocket))
# Send a join message
await websocket.send(json.dumps({"type": "join", "username": username}))
# Main loop to send messages from the user
while True:
message = input("> ")
if message.lower() == 'exit':
break
await websocket.send(json.dumps({"type": "chat", "message": message}))
# Cancel the receive task when we exit
receive_task.cancel()
except websockets.exceptions.ConnectionClosed:
print("Connection to the server was closed.")
except ConnectionRefusedError:
print("Could not connect to the server. Is it running?")
except Exception as e:
print(f"An error occurred: {e}")
async def receive_messages(websocket):
"""A dedicated task to print incoming messages."""
try:
async for message in websocket:
data = json.loads(message)
if data.get("type") == "join":
print(f"*** {data.get('username')} joined the chat. ***")
elif data.get("type") == "chat":
print(f"[{data.get('username')}] {data.get('message')}")
except asyncio.CancelledError:
pass # Task was cancelled, which is expected on exit.
except Exception as e:
print(f"Error receiving message: {e}")
if __name__ == "__main__":
asyncio.run(client())
How to test this:
- Run the
server.py(broadcasting version). - Open two or three different terminal windows.
- In each window, run
python client.pyand enter a different username. - Start typing messages in one client window. You will see the messages appear in all other client windows instantly!
This example demonstrates the core power of WebSockets: real-time, bidirectional communication between multiple clients.
