761  WebSocket Connection Animation

Interactive Visualization of Full-Duplex Real-Time Communication

animation
websocket
protocols
real-time

761.1 WebSocket Connection Lifecycle

This interactive animation demonstrates how WebSocket establishes and maintains full-duplex, real-time connections. Watch the complete lifecycle from HTTP upgrade handshake through bidirectional messaging, heartbeat/keepalive, and graceful close sequences.

NoteWhy WebSocket Matters for IoT

WebSocket provides:

  • Full-Duplex Communication: Both client and server can send data simultaneously without polling
  • Low Overhead: After initial handshake, frames have minimal headers (2-14 bytes)
  • Real-Time Updates: Push data instantly without repeated HTTP requests
  • Persistent Connection: Single TCP connection for the entire session
  • Firewall Friendly: Uses HTTP ports (80/443) for easier traversal

IoT Use Cases: Real-time dashboards, live sensor feeds, remote device control, chat systems, collaborative applications.

TipHow to Use This Animation
  1. Connection States: Watch the connection progress through CONNECTING, OPEN, CLOSING, CLOSED
  2. Frame Types: Select different frame types (text, binary, ping, pong, close) to send
  3. Heartbeat Config: Adjust ping interval and timeout for keepalive demonstration
  4. Reconnection: See exponential backoff retry strategies in action
  5. Play Controls: Use Play/Pause and Step buttons to control animation speed
  6. Scenarios: Try pre-built scenarios for common WebSocket patterns

761.2 WebSocket Protocol Overview

WebSocket (RFC 6455) provides full-duplex communication channels over a single TCP connection. Unlike HTTP’s request-response model, WebSocket allows both client and server to send data at any time.

761.2.1 Connection States

%% fig-alt: "WebSocket connection state machine showing transitions between CONNECTING, OPEN, CLOSING, and CLOSED states. The diagram illustrates the full lifecycle from initial connection through graceful termination."
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22'}}}%%
stateDiagram-v2
    [*] --> CLOSED: Initial State

    CLOSED --> CONNECTING: new WebSocket(url)
    CONNECTING --> OPEN: HTTP 101 Switching Protocols
    CONNECTING --> CLOSED: Connection failed

    OPEN --> OPEN: Send/Receive frames
    OPEN --> CLOSING: close() called
    OPEN --> CLOSED: Connection lost

    CLOSING --> CLOSED: Close handshake complete

    note right of CONNECTING
        Sending HTTP Upgrade request
        Sec-WebSocket-Key header
    end note

    note right of OPEN
        Full-duplex communication
        Ping/Pong heartbeat
    end note

761.2.2 The WebSocket Handshake

WebSocket connections begin with an HTTP Upgrade request:

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

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

The Sec-WebSocket-Key is a random base64-encoded value. The server combines it with a GUID and returns the SHA-1 hash in Sec-WebSocket-Accept. This prevents cache proxy attacks but is not for authentication.

761.3 Frame Types

Opcode Type Description Use Case
0x1 TEXT UTF-8 encoded text data JSON messages, chat
0x2 BINARY Raw binary data Images, files, protobuf
0x8 CLOSE Connection close request Graceful termination
0x9 PING Heartbeat request Keepalive, latency check
0xA PONG Heartbeat response Automatic reply to PING

This diagram shows the binary structure of a WebSocket frame as defined in RFC 6455.

%% fig-alt: Diagram showing WebSocket frame binary structure with FIN bit, opcode, mask flag, payload length, masking key, and payload data fields
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22'}}}%%
flowchart TB
    subgraph Frame["WebSocket Frame Structure"]
        direction LR
        subgraph B1["Byte 1"]
            FIN["FIN<br/>1 bit"]
            RSV["RSV<br/>3 bits"]
            OP["Opcode<br/>4 bits"]
        end
        subgraph B2["Byte 2"]
            MASK["Mask<br/>1 bit"]
            LEN["Length<br/>7 bits"]
        end
        subgraph Extended["Extended (optional)"]
            ELEN["Extended<br/>Length"]
            MKEY["Masking<br/>Key"]
        end
        subgraph Payload["Payload"]
            DATA["Application<br/>Data"]
        end
    end

    B1 --> B2 --> Extended --> Payload

    style Frame fill:#2C3E50,color:#fff
    style B1 fill:#16A085,color:#fff
    style B2 fill:#E67E22,color:#fff
    style Extended fill:#9B59B6,color:#fff
    style Payload fill:#7F8C8D,color:#fff

761.3.1 Frame Structure (Simplified)

 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 ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+

761.4 Heartbeat and Keepalive

WebSocket connections can silently die due to:

  • NAT timeout: Routers close idle connections (often 30-60 seconds)
  • Proxy timeout: Load balancers drop idle connections
  • Network changes: Mobile devices switching networks
  • Half-open connections: Server crash leaving client unaware
TipHeartbeat Best Practices
  1. Ping Interval: 20-30 seconds (before typical NAT timeout)
  2. Pong Timeout: 5-10 seconds (detect dead connections quickly)
  3. Application-level Ping: Consider app-level heartbeats for data integrity
  4. Bidirectional Pings: Both client and server can initiate
// Client-side heartbeat example
const PING_INTERVAL = 30000; // 30 seconds
const PONG_TIMEOUT = 10000;  // 10 seconds

let pingTimer, pongTimer;

function startHeartbeat() {
  pingTimer = setInterval(() => {
    if (ws.readyState === WebSocket.OPEN) {
      ws.send(JSON.stringify({ type: 'ping', ts: Date.now() }));

      pongTimer = setTimeout(() => {
        console.error('Pong timeout - connection dead');
        ws.close();
        reconnect();
      }, PONG_TIMEOUT);
    }
  }, PING_INTERVAL);
}

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type === 'pong') {
    clearTimeout(pongTimer);
    const latency = Date.now() - msg.ts;
    console.log(`RTT: ${latency}ms`);
  }
};

761.5 Reconnection Strategies

When WebSocket connections drop, implement robust reconnection logic:

761.5.1 Exponential Backoff

%% fig-alt: "Flowchart showing WebSocket reconnection strategy with exponential backoff. Shows retry attempts with increasing delays (1s, 2s, 4s, 8s, 16s) and maximum retry limit."
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22'}}}%%
flowchart TD
    DISC[Connection Lost] --> WAIT1["Wait 1s"]
    WAIT1 --> TRY1["Attempt 1"]
    TRY1 -->|Success| OPEN[Connected!]
    TRY1 -->|Fail| WAIT2["Wait 2s"]
    WAIT2 --> TRY2["Attempt 2"]
    TRY2 -->|Success| OPEN
    TRY2 -->|Fail| WAIT4["Wait 4s"]
    WAIT4 --> TRY3["Attempt 3"]
    TRY3 -->|Success| OPEN
    TRY3 -->|Fail| WAIT8["Wait 8s"]
    WAIT8 --> TRY4["...continue"]
    TRY4 -->|Max reached| FAIL[Give Up]
    TRY4 -->|Success| OPEN

    style DISC fill:#E74C3C,color:#fff
    style OPEN fill:#27AE60,color:#fff
    style FAIL fill:#7F8C8D,color:#fff

761.5.2 Reconnection Best Practices

Strategy Description When to Use
Exponential Backoff Double delay each attempt (1s, 2s, 4s, 8s…) Default strategy
Jitter Add randomness to prevent thundering herd Multi-client systems
Max Retries Stop after N attempts Prevent infinite loops
Circuit Breaker Stop retrying after repeated failures Protect servers
Visibility Change Reconnect when tab becomes visible Browser apps
class WebSocketReconnector {
  constructor(url, options = {}) {
    this.url = url;
    this.maxRetries = options.maxRetries || 5;
    this.baseDelay = options.baseDelay || 1000;
    this.maxDelay = options.maxDelay || 30000;
    this.retries = 0;
    this.ws = null;
  }

  connect() {
    this.ws = new WebSocket(this.url);

    this.ws.onopen = () => {
      this.retries = 0; // Reset on successful connection
      console.log('Connected!');
    };

    this.ws.onclose = (event) => {
      if (!event.wasClean && this.retries < this.maxRetries) {
        const delay = Math.min(
          this.baseDelay * Math.pow(2, this.retries),
          this.maxDelay
        );
        // Add jitter (0-100ms)
        const jitter = Math.random() * 100;

        console.log(`Reconnecting in ${delay + jitter}ms...`);
        setTimeout(() => {
          this.retries++;
          this.connect();
        }, delay + jitter);
      }
    };
  }
}

761.6 WebSocket vs Other Protocols

Feature WebSocket HTTP Long Polling Server-Sent Events MQTT over WS
Direction Full-duplex Half-duplex Server-to-client Full-duplex
Overhead 2-14 bytes/frame Full HTTP headers Minimal MQTT + WS headers
Latency Very low Higher Low Low
Reconnection Manual Automatic Automatic Handled by MQTT
Browser Support All modern All Most modern Via library
IoT Fit Good Poor Good for push Excellent
ImportantWhen to Use WebSocket for IoT

Best For: - Real-time dashboards with bidirectional control - Browser-based device management - Live sensor visualization - Chat and collaboration features

Consider Alternatives: - MQTT: Better for constrained devices, built-in QoS - CoAP: Better for low-power wireless networks - Server-Sent Events: Simpler if you only need server push

761.7 IoT Use Cases

761.7.1 Real-Time Dashboard

%% fig-alt: "Architecture diagram showing IoT sensors sending data through MQTT broker to a WebSocket gateway, which streams to a browser dashboard for real-time visualization."
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22'}}}%%
flowchart LR
    subgraph Devices["IoT Devices"]
        S1["🌡️ Temp"]
        S2["💧 Humidity"]
        S3["💨 CO2"]
    end

    subgraph Backend["Backend"]
        MQTT["MQTT Broker"]
        WS["WebSocket<br/>Gateway"]
    end

    subgraph Browser["Browser Dashboard"]
        UI["📊 Charts"]
        CTRL["🎛️ Controls"]
    end

    S1 --> MQTT
    S2 --> MQTT
    S3 --> MQTT
    MQTT --> WS
    WS <-->|Full-duplex| UI
    WS <-->|Commands| CTRL

    style Devices fill:#16A085,color:#fff
    style Backend fill:#2C3E50,color:#fff
    style Browser fill:#E67E22,color:#fff

761.7.2 Remote Device Control

// Browser-side device control
const ws = new WebSocket('wss://iot.example.com/device/123');

ws.onopen = () => {
  // Subscribe to device telemetry
  ws.send(JSON.stringify({
    type: 'subscribe',
    topics: ['temperature', 'status']
  }));
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  updateDashboard(data);
};

// Send command to device
function setTemperature(value) {
  ws.send(JSON.stringify({
    type: 'command',
    action: 'setTemperature',
    value: value
  }));
}

761.8 Conceptual Diagrams

%% fig-alt: Process flow diagram showing WebSocket connection from HTTP upgrade handshake through bidirectional communication
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22', 'secondaryColor': '#ECF0F1', 'tertiaryColor': '#fff'}}}%%
flowchart TD
    subgraph Handshake["HTTP Upgrade Handshake"]
        A[Client opens TCP connection] --> B[Send HTTP Upgrade request]
        B --> C[Server validates request]
        C --> D[Server sends 101 Switching Protocols]
        D --> E[WebSocket connection established]
    end

    subgraph Communication["Bidirectional Communication"]
        E --> F{Who sends?}
        F --> |Client|G[Client sends frame]
        F --> |Server|H[Server sends frame]
        G --> I[Frame delivered to server]
        H --> J[Frame delivered to client]
        I --> F
        J --> F
    end

    subgraph Maintenance["Connection Maintenance"]
        K[Ping frame sent] --> L[Pong frame returned]
        L --> M[Connection healthy]
        M --> K
    end

    subgraph Close["Connection Close"]
        N[Close frame sent] --> O[Close frame acknowledged]
        O --> P[TCP connection closed]
    end

    style Handshake fill:#2C3E50,color:#fff
    style Communication fill:#16A085,color:#fff
    style Maintenance fill:#E67E22,color:#fff
    style Close fill:#7F8C8D,color:#fff

%% fig-alt: State diagram showing WebSocket connection states from CONNECTING through OPEN to CLOSED
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22'}}}%%
stateDiagram-v2
    [*] --> CONNECTING: new WebSocket(url)
    CONNECTING --> OPEN: 101 Switching Protocols
    CONNECTING --> CLOSED: Connection failed

    OPEN --> OPEN: Send/receive frames
    OPEN --> CLOSING: close() called
    OPEN --> CLOSED: Connection lost

    CLOSING --> CLOSED: Close handshake complete
    CLOSED --> [*]

    note right of CONNECTING : readyState = 0
    note right of OPEN : readyState = 1
    note right of CLOSING : readyState = 2
    note right of CLOSED : readyState = 3

%% fig-alt: Comparison diagram showing WebSocket persistent connection versus HTTP request-response cycle
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22'}}}%%
flowchart LR
    subgraph HTTP["HTTP Polling"]
        H1[Request] --> H2[Response]
        H3[Request] --> H4[Response]
        H5[Request] --> H6[Response]
        H7[Overhead per message]
    end

    subgraph WS["WebSocket"]
        W1[Upgrade once] --> W2[Message]
        W2 --> W3[Message]
        W3 --> W4[Message]
        W4 --> W5[Message]
        W6[Low overhead per message]
    end

    Compare[Choose Based On]
    Compare --> |Frequent updates|WS
    Compare --> |Rare updates|HTTP

    HTTP --> Use1[Use for:<br/>REST APIs<br/>Infrequent data<br/>Request-response]
    WS --> Use2[Use for:<br/>Real-time data<br/>IoT dashboards<br/>Live updates]

    style HTTP fill:#7F8C8D,color:#fff
    style WS fill:#16A085,color:#fff

%% fig-alt: Architecture diagram showing WebSocket frame structure with opcode, payload, and masking
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22'}}}%%
flowchart TB
    subgraph Frame["WebSocket Frame"]
        direction LR
        FIN[FIN<br/>1 bit]
        RSV[RSV1-3<br/>3 bits]
        OP[Opcode<br/>4 bits]
        MASK[Mask<br/>1 bit]
        LEN[Payload Length<br/>7+ bits]
        KEY[Masking Key<br/>0 or 4 bytes]
        DATA[Payload Data]
    end

    subgraph Opcodes["Frame Types"]
        T0[0x0: Continuation]
        T1[0x1: Text]
        T2[0x2: Binary]
        T8[0x8: Close]
        T9[0x9: Ping]
        TA[0xA: Pong]
    end

    Frame --> Opcodes

    style Frame fill:#2C3E50,color:#fff
    style Opcodes fill:#16A085,color:#fff

761.9 What’s Next


This animation is implemented in approximately 800 lines of Observable JavaScript. Key features:

  1. Connection State Machine: Accurate representation of WebSocket readyState (CONNECTING=0, OPEN=1, CLOSING=2, CLOSED=3)
  2. Frame Type Visualization: All RFC 6455 frame types with color coding
  3. Heartbeat Simulation: Configurable ping interval with timeout detection
  4. Reconnection Demo: Exponential backoff with visual countdown
  5. Play/Pause Controls: Speed adjustment and step-through mode
  6. Event Logging: Real-time log of all WebSocket events
  7. IEEE Colors: Consistent palette throughout

The animation demonstrates the complete WebSocket lifecycle from handshake through graceful close, with emphasis on the reliability features critical for IoT applications.