---
title: "WebSockets: Full-Duplex Real-Time Communication"
source: Panaversity Learn Agentic AI
source_url: https://github.com/panaversity/learn-agentic-ai
licence: Apache-2.0
domain: agentic-ai
subdomain: panaversity-agentic
date_added: 2026-04-25
---

# WebSockets: Full-Duplex Real-Time Communication

WebSockets (RFC 6455) is a communication protocol that provides a **full-duplex communication channel** over a single, long-lived TCP connection. It is designed to be implemented in web browsers and web servers but can be used by any client or server application. Unlike traditional HTTP request-response, WebSockets allow for bi-directional, real-time data exchange once the connection is established.

This makes WebSockets ideal for applications requiring low-latency, high-frequency updates, such as live chat, online gaming, real-time data feeds, collaborative editing tools, and interactive agentic systems.

---

## Core Concepts

1.  **The WebSocket Handshake (Upgrade)**:

    - The WebSocket connection starts as a standard HTTP GET request from the client to the server.
    - This initial request includes specific headers (`Upgrade: websocket`, `Connection: Upgrade`, `Sec-WebSocket-Key`, `Sec-WebSocket-Version`) signaling the client's intent to establish a WebSocket connection.
    - If the server supports WebSockets and agrees to the upgrade, it responds with an HTTP `101 Switching Protocols` status code, along with its own specific headers (`Sec-WebSocket-Accept`).
    - Once the handshake is complete, the underlying TCP connection is repurposed for WebSocket messages, and it's no longer an HTTP connection.

2.  **Persistent Connection**:

    - After the handshake, the TCP connection remains open, allowing both the client and server to send data at any time until one party decides to close it.

3.  **Full-Duplex Communication**:

    - Data can be sent and received simultaneously by both client and server over the same connection.

4.  **Frames and Messages**:

    - WebSocket communication consists of "frames." A message can be split into multiple frames.
    - Frames can be of different types: text (UTF-8), binary, ping, pong (for keep-alive), and close.
    - This framing mechanism allows for efficient transmission of both small and large messages.

5.  **Subprotocols (`Sec-WebSocket-Protocol`)**:

    - Clients and servers can agree on an application-level subprotocol to structure their messages (e.g., using JSON, Protobuf, or custom formats over WebSockets). This is negotiated during the handshake.

6.  **Security (`ws://` and `wss://`)**:
    - `ws://` denotes an unencrypted WebSocket connection.
    - `wss://` denotes a secure WebSocket connection, encrypted using TLS (Transport Layer Security), similar to HTTPS. This is crucial for protecting sensitive data.

---

## Working with WebSockets in Python: `websockets` Library

The [`websockets`](https://websockets.readthedocs.io/) library is a popular and robust choice for building WebSocket clients and servers in Python using `asyncio`.

### Installation

```bash
pip install websockets
# or using uv
# uv pip install websockets
```

### Example: Hands-on WebSocket Server

This server listens for connections, echoes received messages back to the client, and logs connection activity.

**File:** `websocket_server.py`

```python
import asyncio
import websockets
import logging

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - SERVER - %(levelname)s - %(message)s')

connections = set()

async def handler(websocket, path):
    """
    Handles incoming WebSocket connections, registers them,
    echoes messages, and unregisters on disconnection.
    """
    connections.add(websocket)
    logging.info(f"Client connected from {websocket.remote_address}. Path: {path}. Total connections: {len(connections)}")
    try:
        async for message in websocket:
            logging.info(f"Received message from {websocket.remote_address}: {message}")
            # Echo the message back to the sender
            await websocket.send(f"Server echoes: {message}")
            logging.info(f"Echoed message to {websocket.remote_address}: {message}")

            # Example of broadcasting to all other clients (except sender)
            # for conn in connections:
            #     if conn != websocket:
            #         await conn.send(f"Broadcast from {websocket.remote_address}: {message}")
    except websockets.exceptions.ConnectionClosedOK:
        logging.info(f"Client {websocket.remote_address} disconnected gracefully.")
    except websockets.exceptions.ConnectionClosedError as e:
        logging.info(f"Client {websocket.remote_address} connection closed with error: {e}")
    except Exception as e:
        logging.error(f"An unexpected error occurred with client {websocket.remote_address}: {e}")
    finally:
        connections.remove(websocket)
        logging.info(f"Client {websocket.remote_address} removed. Total connections: {len(connections)}")

async def main():
    # Start the WebSocket server
    logging.info("Starting WebSocket server on ws://localhost:8765")
    async with websockets.serve(handler, "localhost", 8765):
        await asyncio.Future()  # Run forever

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        logging.info("WebSocket server shutting down.")
```

**To run this server:**

1. Save the code above as `16_WebSockets/websocket_server.py`.
2. Run from your terminal: `python 16_WebSockets/websocket_server.py`
   The server will start and log "WebSocket server running on ws://localhost:8765".

### Example: Hands-on WebSocket Client

This client connects to the server, sends a series of messages, prints the received echoes, and then closes the connection.

**File:** `websocket_client.py`

```python
import asyncio
import websockets
import logging

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - CLIENT - %(levelname)s - %(message)s')

async def send_and_receive_messages(uri):
    """
    Connects to the WebSocket server, sends a few messages,
    and receives responses.
    """
    try:
        async with websockets.connect(uri) as websocket:
            logging.info(f"Connected to WebSocket server at {uri}")

            messages_to_send = [
                "Hello WebSocket Server!",
                "This is a test message.",
                "How are you today?",
                "QUIT" # A special message to gracefully close from client side if server handles it
            ]

            for message in messages_to_send:
                if not websocket.open:
                    logging.warning("Connection closed before sending all messages.")
                    break

                logging.info(f"Sending message: '{message}'")
                await websocket.send(message)

                try:
                    response = await asyncio.wait_for(websocket.recv(), timeout=5.0)
                    logging.info(f"Received response: '{response}'")
                except asyncio.TimeoutError:
                    logging.error("Timeout waiting for server response.")
                    break # Stop if server isn't responding
                except websockets.exceptions.ConnectionClosed:
                    logging.warning("Connection closed by server while waiting for response.")
                    break

                await asyncio.sleep(1) # Small delay between messages

            # Explicitly close the connection if it's still open
            if websocket.open:
                logging.info("Closing WebSocket connection.")
                await websocket.close(code=1000, reason="Client finished")

    except websockets.exceptions.ConnectionClosedError as e:
        logging.error(f"Connection to {uri} closed with error: {e}")
    except websockets.exceptions.InvalidURI:
        logging.error(f"Invalid WebSocket URI: {uri}")
    except ConnectionRefusedError:
        logging.error(f"Connection refused by the server at {uri}. Is the server running?")
    except Exception as e:
        logging.error(f"An unexpected error occurred: {e}")

async def main():
    server_uri = "ws://localhost:8765"
    await send_and_receive_messages(server_uri)

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        logging.info("WebSocket client shutting down.")
```

**To run this client:**

1. Save the code above as `16_WebSockets/websocket_client.py`.
2. Ensure the `websocket_server.py` is already running in another terminal.
3. Run from your terminal: `python 16_WebSockets/websocket_client.py`

---

## Strengths of WebSockets

- **Low Latency**: After the initial handshake, messages are exchanged with minimal overhead compared to HTTP polling or long-polling, as TCP headers are much smaller than HTTP headers.
- **True Bidirectional (Full-Duplex)**: Both client and server can send messages independently and simultaneously once the connection is established.
- **Efficiency**: Reduces network traffic and server load compared to techniques that repeatedly establish new HTTP connections (polling) or keep connections open with higher overhead (long-polling).
- **Real-Time Interactivity**: Ideal for applications that require immediate feedback or updates (e.g., chat, live sports scores, collaborative document editing).
- **Stateful Connections**: The persistent connection allows for maintaining session state on the server associated with each connection.
- **Standardized**: Widely adopted and supported natively in all modern web browsers and by many server-side frameworks.
- **Works Over HTTP Ports**: Typically uses ports 80 (ws) and 443 (wss), which are usually open in firewalls, making it less likely to be blocked.

## Weaknesses and Considerations

- **Persistent Connections and Scalability**: Maintaining many open connections can be resource-intensive on the server (memory, CPU). Scaling WebSocket servers requires careful architecture (e.g., using load balancers that support WebSockets, distributed session management).
- **State Management**: Being stateful, if a server node fails, client connections to that node are lost. Reconnection logic and state recovery mechanisms are often needed.
- **Proxy and Firewall Traversal (Historically)**: Older proxies might not understand the HTTP Upgrade mechanism or might terminate long-lived connections. This is less of an issue with modern infrastructure but can still be a concern in some corporate environments. `wss://` (secure WebSockets) helps as it looks like HTTPS traffic.
- **No Built-in Request-Response Matching (like HTTP)**: While you can implement request-response patterns over WebSockets (e.g., by adding message IDs), it's not an inherent feature as with HTTP. This means more application-level logic for message correlation if needed.
- **Complexity for Simple Use Cases**: For simple unidirectional server-to-client push where the client doesn't need to send much data back, Server-Sent Events (SSE) can be a simpler alternative.

## Common Use Cases

- **Real-time Chat Applications**: Instant messaging, group chats.
- **Online Gaming**: Multiplayer games requiring low-latency updates of game state.
- **Live Data Feeds**: Stock tickers, sports scores, news updates, IoT sensor data.
- **Collaborative Tools**: Real-time co-editing of documents (like Google Docs), shared whiteboards.
- **Notifications**: Pushing alerts and updates to clients without requiring them to poll.
- **Remote Control/Monitoring**: Controlling devices or monitoring systems in real-time.

## WebSockets in DACA and A2A Communication

Within the Dapr Agentic Cloud Ascent (DACA) framework, WebSockets offer a powerful mechanism for real-time, bidirectional Agent-to-Agent (A2A) communication.

**When to Consider WebSockets for A2A in DACA:**

1.  **True Full-Duplex Needs**: When agents need to send and receive messages asynchronously and frequently, without the overhead of repeated HTTP requests or the unidirectional nature of SSE. For example, a control agent sending commands to an operational agent while simultaneously receiving status updates.
2.  **Low-Latency Requirements**: For interactions where near-instantaneous communication is critical, such as coordinating fast-paced simulations or real-time control loops between agents.
3.  **Persistent Interaction Channels**: If agents need to maintain an "open line" for an extended period for ongoing dialogue or data exchange.
4.  **Streaming Large, Continuous Data**: While HTTP streaming can handle this, WebSockets can provide a more integrated channel if the interaction is already bidirectional.

**Comparison with other DACA communication options:**

- **vs. Streamable HTTP (MCP-style, Single Endpoint with SSE)**:

  - Streamable HTTP is excellent for request-response that can optionally become a server-to-client stream (SSE) or for a client to open an SSE stream for server-initiated messages.
  - WebSockets provide a more symmetrical, always-on bidirectional channel from the outset. If both agents are equally likely to initiate communication at any time over the same "session," WebSockets might be more direct.
  - Streamable HTTP might be simpler if one side is predominantly a "server" and the other a "client," even if the server can push messages.

- **vs. Server-Sent Events (SSE) directly**:

  - SSE is simpler for one-way server-to-client push. If Agent B only needs to receive updates from Agent A and rarely sends data back (other than the initial connection), SSE is more lightweight.
  - WebSockets are necessary if Agent B also needs to send frequent, unsolicited messages to Agent A.

- **vs. gRPC Streaming**:

  - gRPC offers bidirectional streaming with the benefits of Protobuf (strong typing, efficiency). It's excellent for internal microservice communication.
  - WebSockets might be preferred if one of the agents is a browser-based UI or if a simpler text-based protocol (like JSON over WebSockets) is desired for wider compatibility or easier debugging. Dapr can invoke gRPC services, so this is a strong contender within DACA.

- **vs. Message Queues (e.g., Kafka, RabbitMQ via Dapr Pub/Sub)**:
  - Message queues provide durable, asynchronous communication, excellent for decoupling agents, handling backpressure, and ensuring message delivery even if an agent is temporarily offline.
  - WebSockets are for direct, online, real-time communication between currently connected agents. They are not inherently durable.
  - These can be complementary: agents might communicate in real-time via WebSockets for an active task, but use pub/sub for more general event notifications or a "task queue."

**Dapr Integration:**
Dapr itself doesn't have a specific "WebSocket" building block in the same way it has pub/sub or service invocation. However:

- Agents built as services (e.g., FastAPI, ASP.NET Core) can expose WebSocket endpoints.
- Dapr service invocation can call standard HTTP endpoints, so the initial WebSocket handshake (which starts as HTTP) can be managed. Once upgraded, the connection is direct between the Dapr sidecars (if app-to-app API) or client-to-sidecar-to-app.
- Dapr's mTLS can secure the communication between sidecars, thus securing the WebSocket traffic within the service mesh.

**Payload Format:**
Just like with other transports in DACA, if agents communicate via WebSockets, they would need to agree on a payload format (e.g., JSON-RPC, custom JSON, Protobuf).

**Conclusion for DACA:**
WebSockets are a valuable tool in the DACA arsenal for A2A communication, especially when low-latency, true bidirectional interaction is paramount. They should be chosen when the stateful, persistent nature of the connection is an advantage for the specific interaction pattern, and alternatives like Streamable HTTP or SSE don't fully meet the bidirectional requirements.

---

## Place in the Protocol Stack

- **Layer**: Application Layer (OSI Layer 7). It relies on TCP (Layer 4) for reliable transport.
- **Above**: Custom application logic, agent frameworks, messaging formats (like JSON, XML, Protobuf if used as payload).
- **Below**: The initial handshake uses HTTP/1.1. After the upgrade, it directly uses TCP. If `wss://` is used, TLS is employed between WebSockets and TCP.

---

## Further Reading

- [RFC 6455: The WebSocket Protocol](https://datatracker.ietf.org/doc/html/rfc6455) (The official specification)
- [MDN Web Docs: WebSockets API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) (Excellent resource for client-side JavaScript and general concepts)
- [`websockets` library documentation (Python)](https://websockets.readthedocs.io/en/stable/)
- [Wikipedia: WebSocket](https://en.wikipedia.org/wiki/WebSocket)
