WebSocket Β· Astro Tech Blog

WebSocket

WebSocket provides full-duplex communication over a single TCP connection β€” the server can push data to the client at any time, without polling.

How WebSocket Works

HTTP Upgrade:
Client                              Server
  β”‚                                   β”‚
  β”œβ”€β”€ GET /chat (HTTP) ──────────────▢│
  β”‚   Upgrade: websocket              β”‚
  β”‚   Connection: Upgrade             β”‚
  β”‚                                   β”‚
  │◀── 101 Switching Protocols ───────
  β”‚   Upgrade: websocket              β”‚
  β”‚                                   β”‚
  β”œβ”€β”€ πŸ”„ WebSocket connection open ──▢│  (bidirectional)
  │◀── message ───────────────────────
  β”œβ”€β”€ message ──────────────────────▢│
  │◀── message ───────────────────────
  β”‚                                   β”‚
  │◀── close ─────────────────────────
  β”‚  (connection closed)              β”‚

Connecting

const socket = new WebSocket('wss://echo.example.com');

socket.onopen = function() {
  console.log('Connected');
  socket.send('Hello server!');
};

socket.onmessage = function(event) {
  console.log('Received:', event.data);
};

socket.onerror = function(error) {
  console.error('WebSocket error:', error);
};

socket.onclose = function(event) {
  console.log('Disconnected:', event.code, event.reason);
};
Demo: WebSocket Echo Demo
HTML
<div>
<p>Connect to a public echo WebSocket server and send messages.</p>
<div style='display:flex;gap:8px;margin-bottom:8px;'>
<button id='ws-connect'>Connect</button>
<button id='ws-disconnect' disabled>Disconnect</button>
</div>
<div style='display:flex;gap:8px;margin-bottom:8px;'>
<input id='ws-message' type='text' value='Hello WebSocket!' disabled style='flex:1;padding:6px;border:1px solid #cbd5e1;border-radius:4px;'>
<button id='ws-send' disabled>Send</button>
</div>
<div style='max-height:150px;overflow-y:auto;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;padding:8px;'>
<div id='ws-log' style='font-family:monospace;font-size:0.85rem;'></div>
</div>
<pre id='ws-status' style='background:#f1f5f9;padding:8px;border-radius:4px;margin-top:8px;'></pre>
</div>
JavaScript
const log = document.getElementById('ws-log');
const status = document.getElementById('ws-status');
let ws = null;

function addLog(msg, type) {
const div = document.createElement('div');
div.style.color = type === 'sent' ? '#6366f1' : type === 'received' ? '#059669' : '#64748b';
div.textContent = '[' + new Date().toLocaleTimeString() + '] ' + msg;
log.insertBefore(div, log.firstChild);
}

document.getElementById('ws-connect').onclick = function() {
try {
ws = new WebSocket('wss://echo.websocket.org/?encoding=text');

ws.onopen = function() {
addLog('Connected to server', 'system');
status.textContent = 'βœ… Connected';
document.getElementById('ws-connect').disabled = true;
document.getElementById('ws-disconnect').disabled = false;
document.getElementById('ws-send').disabled = false;
document.getElementById('ws-message').disabled = false;
};

ws.onmessage = function(e) {
addLog('Received: ' + e.data, 'received');
};

ws.onerror = function() {
addLog('WebSocket error occurred', 'system');
status.textContent = '❌ Error (server may be unavailable)';
};

ws.onclose = function(e) {
addLog('Disconnected (code: ' + e.code + ')', 'system');
status.textContent = '❌ Disconnected';
document.getElementById('ws-connect').disabled = false;
document.getElementById('ws-disconnect').disabled = true;
document.getElementById('ws-send').disabled = true;
document.getElementById('ws-message').disabled = true;
};
} catch (err) {
status.textContent = '❌ Error: ' + err.message;
}
};

document.getElementById('ws-disconnect').onclick = function() {
if (ws) {
ws.close(1000, 'User disconnected');
ws = null;
}
};

document.getElementById('ws-send').onclick = function() {
const msg = document.getElementById('ws-message').value;
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(msg);
addLog('Sent: ' + msg, 'sent');
} else {
status.textContent = '⚠️ Not connected';
}
};
Live Output Window

WebSocket Events

EventDescription
openConnection established
messageData received (event.data)
errorError occurred
closeConnection closed (event.code, event.reason, event.wasClean)

WebSocket States

WebSocket.CONNECTING // 0 β€” connecting
WebSocket.OPEN       // 1 β€” open, ready to send/receive
WebSocket.CLOSING    // 2 β€” closing
WebSocket.CLOSED     // 3 β€” closed

socket.readyState    // current state

Sending Data

// String
socket.send('Hello');

// JSON
socket.send(JSON.stringify({ type: 'chat', message: 'Hello' }));

// Binary
socket.send(new Blob([data]));
socket.send(new Uint8Array([1, 2, 3]));

// ArrayBuffer
socket.send(buffer);

Receiving Data

The event.data type depends on what the server sent:

socket.onmessage = function(event) {
  if (typeof event.data === 'string') {
    // Text message
    const data = JSON.parse(event.data);
  } else if (event.data instanceof Blob) {
    // Binary blob
    const text = await event.data.text();
  } else if (event.data instanceof ArrayBuffer) {
    // Binary buffer
    const view = new Uint8Array(event.data);
  }
};

Automatic Reconnection

WebSocket doesn’t auto-reconnect. Implement it yourself:

function connectWithRetry(url, maxRetries = 5) {
  let attempts = 0;

  function connect() {
    const socket = new WebSocket(url);

    socket.onopen = () => {
      attempts = 0;
      console.log('Connected');
    };

    socket.onclose = (e) => {
      if (e.code !== 1000 && attempts < maxRetries) {
        attempts++;
        const delay = Math.min(1000 * Math.pow(2, attempts), 30000);
        console.log(`Reconnecting in ${delay}ms (attempt ${attempts})`);
        setTimeout(connect, delay);
      }
    };

    socket.onerror = () => { /* onclose will fire */ };
  }

  connect();
}

Subprotocols

const socket = new WebSocket('wss://example.com/chat', ['chat', 'v2']);

The server can choose which subprotocol to use:

socket.onopen = function() {
  console.log('Negotiated subprotocol:', socket.protocol);
};

Key Takeaways

  • WebSocket provides bidirectional, low-latency communication
  • Connect with new WebSocket('wss://...')
  • Handle open, message, error, close events
  • Send strings or binary data with socket.send()
  • WebSocket does not auto-reconnect β€” implement it yourself
  • Use wss:// (secure) instead of ws:// in production
  • Parse JSON manually: JSON.parse(event.data)
  • For simple serverβ†’client updates, consider Server-Sent Events as a simpler alternative