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
| Event | Description |
|---|---|
open | Connection established |
message | Data received (event.data) |
error | Error occurred |
close | Connection 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,closeevents - Send strings or binary data with
socket.send() - WebSocket does not auto-reconnect β implement it yourself
- Use
wss://(secure) instead ofws://in production - Parse JSON manually:
JSON.parse(event.data) - For simple serverβclient updates, consider Server-Sent Events as a simpler alternative