🚀 WebSockets Tutorial: Real-Time Communication in Python

✅ This tutorial explains WebSockets, how they differ from SSE & REST, and provides a full working example using FastAPI, WebSockets, and Streamlit UI.
✅ Includes Docker Compose to make deployment easy!


🔹 What are WebSockets?

WebSockets allow bidirectional, full-duplex communication between client and server over a single TCP connection.

✅ Persistent connection (unlike REST which closes after response)
✅ Two-way communication (unlike SSE, which is server → client only)
✅ Efficient for real-time data (chat apps, live stock prices, multiplayer gaming, etc.)


🔹 WebSockets vs SSE vs REST

Feature WebSockets SSE (Server-Sent Events) REST API
Connection Type Persistent Persistent Short-lived
Data Flow Two-Way (Client ↔ Server) One-Way (Server → Client) One-Way (Client → Server)
Use Case Chat, multiplayer games, live collaboration Live notifications, real-time feeds Standard CRUD operations
Efficiency ✅ High ✅ Medium ❌ Low (due to polling)

📌 Step 1: Install Dependencies

Install FastAPI, WebSockets, and Streamlit: sh pip install fastapi uvicorn streamlit websockets


📌 Step 2: Create WebSocket Server (ws_server.py)

✅ Handles WebSocket connections
✅ Broadcasts messages to all connected clients

from fastapi import FastAPI, WebSocket, WebSocketDisconnect
import asyncio

app = FastAPI()
connected_clients = []  # List of connected WebSocket clients

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    """Handles WebSocket connections."""
    await websocket.accept()
    connected_clients.append(websocket)
    try:
        while True:
            data = await websocket.receive_text()  # Receive message from client
            print(f"🔹 Received: {data}")
            for client in connected_clients:
                await client.send_text(f"📩 {data}")  # Broadcast message to all clients
    except WebSocketDisconnect:
        connected_clients.remove(websocket)
        print("❌ Client disconnected")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

✅ WebSocket Clients stay connected and receive real-time updates.
✅ When a user sends a message, it broadcasts to all clients.


📌 Step 3: Create WebSocket Client (ws_client.py)

✅ This client connects to the WebSocket server and listens for messages.

import asyncio
import websockets

async def listen_to_server():
    uri = "ws://localhost:8000/ws"
    async with websockets.connect(uri) as websocket:
        while True:
            message = await websocket.recv()
            print(f"📩 Received from Server: {message}")

asyncio.run(listen_to_server())

✅ This client continuously listens for real-time messages from the server.


📌 Step 4: Create WebSocket Streamlit Frontend (frontend.py)

✅ Allows users to send messages
✅ Displays incoming WebSocket messages live

import streamlit as st
import asyncio
import websockets
import threading

st.title("📡 WebSocket Real-Time Chat")

# Display messages container
messages_container = st.empty()

# WebSocket connection
ws_url = "ws://ws-server:8000/ws"

# Function to send a WebSocket message
async def send_message(msg):
    async with websockets.connect(ws_url) as ws:
        await ws.send(msg)

# Function to receive WebSocket messages
def receive_messages():
    async def listen():
        async with websockets.connect(ws_url) as ws:
            while True:
                message = await ws.recv()
                messages_container.text(message)
    
    asyncio.run(listen())

# Start listening for messages in a separate thread
thread = threading.Thread(target=receive_messages, daemon=True)
thread.start()

# Input box for sending messages
user_input = st.text_input("Enter your message:")
if st.button("Send"):
    asyncio.run(send_message(user_input))
    st.success("Message Sent!")

✅ Allows users to send messages via WebSockets
✅ Live updates appear instantly for all connected users


📌 Step 5: Create docker-compose.yml

✅ WebSocket Server, Client, and Frontend are now fully containerized!

version: '3.8'

services:
  ws-server:
    build: .
    container_name: ws-server
    ports:
      - "8000:8000"

  ws-client:
    build: .
    container_name: ws-client
    depends_on:
      - ws-server
    entrypoint: ["python"]
    command: ["ws_client.py"]

  frontend:
    build: .
    container_name: ws-frontend
    depends_on:
      - ws-server
    ports:
      - "8501:8501"
    entrypoint: ["python"]
    command: ["frontend.py"]

✅ Fully containerized setup for instant real-time communication 🚀


📌 Step 6: Running Everything

✅ Start the WebSocket System

docker-compose up --build

✅ Starts:
- WebSocket Server (http://localhost:8000/ws)
- Streamlit Frontend (http://localhost:8501)


📌 Step 7: Testing

✅ Test WebSocket Client

Run: sh docker logs -f ws-client Expected output: 📩 Received from Server: Hello WebSockets! 📩 Received from Server: Welcome to real-time chat!

✅ Send & Receive Messages via UI

1️⃣ Go to http://localhost:8501
2️⃣ Enter a message in the text box and click "Send"
3️⃣ Message should instantly appear on all connected clients!


🚀 Final Takeaway

✅ WebSockets enable full-duplex communication (better than REST & SSE for real-time apps).
✅ WebSockets instantly push updates to clients (no polling needed).
✅ Now you have a fully functional WebSocket chat system!

🚀 Now you truly understand WebSockets! 🎯🔥
Let me know if you need modifications! 😊🚀