Skip to main content
โšก Calmops

WebSocket Protocol: Persistent Connections 2026

Introduction

WebSocket is a computer communications protocol that provides full-duplex communication channels over a single TCP connection. Unlike HTTP’s request-response model, WebSocket enables persistent bidirectional communication between client and server, making it ideal for real-time applications.

This comprehensive guide covers WebSocket protocol mechanics, handshake process, frame format, security considerations, and implementation patterns.

What is WebSocket?

WebSocket was standardized by the IETF as RFC 6455 in 2011, providing a standardized way for web browsers and servers to communicate over a single, long-lived TCP connection.

Key Characteristics

Full Duplex: Bidirectional communication simultaneously.

Persistent Connection: Single handshake, persistent connection.

Low Overhead: No HTTP headers after establishment.

Cross-Origin: Works across origins with proper headers.

Use Cases

  • Real-time chat applications
  • Live streaming dashboards
  • Online gaming
  • Financial trading platforms
  • Collaborative editing tools
  • IoT device communication

Protocol Mechanics

Handshake

The WebSocket handshake begins as an HTTP request and upgrades to WebSocket protocol.

HTTP Request:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: http://example.com

HTTP Response:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

JavaScript Implementation

// Client-side WebSocket
const ws = new WebSocket('wss://server.example.com/chat');

ws.onopen = () => {
    console.log('WebSocket connected');
    ws.send(JSON.stringify({ type: 'join', room: 'lobby' }));
};

ws.onmessage = (event) => {
    const data = JSON.parse(event.data);
    console.log('Received:', data);
};

ws.onerror = (error) => {
    console.error('WebSocket error:', error);
};

ws.onclose = (event) => {
    console.log('Disconnected:', event.code, event.reason);
};

// Send message
ws.send(JSON.stringify({ message: 'Hello' }));

// Close connection
ws.close(1000, 'Closing normally');

Frame Format

WebSocket frames have a compact binary format:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len|    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|                         Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               | Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+---------------------------------------------------------------+

Frame Types

Opcode Meaning
0x0 Continuation frame
0x1 Text frame
0x2 Binary frame
0x8 Connection close
0x9 Ping
0xA Pong

Python Server

import asyncio
import websockets

async def chat_handler(websocket, path):
    # Handle client connection
    try:
        async for message in websocket:
            # Parse incoming message
            data = json.loads(message)
            
            if data['type'] == 'message':
                # Broadcast to all clients
                await broadcast(message)
            elif data['type'] == 'ping':
                await websocket.send(json.dumps({'type': 'pong'}))
                
    except websockets.exceptions.ConnectionClosed:
        print('Client disconnected')

async def main():
    async with websockets.serve(chat_handler, "localhost", 8765):
        await asyncio.Future()  # Run forever

asyncio.run(main())

WebSocket Secure (WSS)

// Always use WSS in production
const ws = new WebSocket('wss://secure.example.com/ws');

// With authentication
const ws = new WebSocket('wss://api.example.com/ws', {
    headers: {
        'Authorization': `Bearer ${token}`
    }
});

Subprotocols

Using Subprotocols

// Client requests subprotocol
const ws = new WebSocket('wss://server.example.com', ['soap', 'wamp']);

// Server selects one
// Sec-WebSocket-Protocol: wamp

// Then use as WAMP client
ws.onmessage = (event) => {
    const msg = JSON.parse(event.data);
    if (msg[0] === 16) {  // HELLO
        // WAMP session established
    }
};

Common Subprotocols

  • WAMP (WebSocket Application Messaging Protocol)
  • SOAP over WebSocket
  • MQTT over WebSocket

Scalability

Pub/Sub with Redis

import asyncio
import aioredis
import websockets
import json

class ChatServer:
    def __init__(self):
        self.redis = None
        self.subscriptions = {}
    
    async def connect(self, websocket, path):
        client_id = id(websocket)
        
        async for message in websocket:
            data = json.loads(message)
            
            if data['type'] == 'subscribe':
                channel = data['channel']
                await self.redis.subscribe(channel)
                self.subscriptions[client_id] = channel
                
            elif data['type'] == 'publish':
                channel = data['channel']
                message = data['message']
                await self.redis.publish(channel, json.dumps(message))
    
    async def handle_redis_message(self, channel, message):
        # Broadcast to WebSocket clients
        for ws in self.clients:
            await ws.send(message)

Load Balancing

# nginx WebSocket proxy
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 443 ssl http2;
    
    location /ws/ {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        
        proxy_read_timeout 86400;
        proxy_send_timeout 86400;
    }
}

Best Practices

  • Always use WSS in production
  • Implement heartbeat/ping-pong
  • Handle reconnection gracefully
  • Validate all incoming data
  • Set appropriate timeouts
  • Monitor connection states

Conclusion

WebSocket enables powerful real-time applications with persistent bidirectional communication. Its widespread browser support and simple API make it the go-to choice for building interactive web applications.

Resources

Comments