乐闻世界logo
搜索文章和话题

How to use WebSocket in browser?

2月18日 18:57

WebSocket API is part of HTML5, and all modern browsers support WebSocket.

Basic Usage

Creating WebSocket Connection

javascript
// Create WebSocket connection const ws = new WebSocket('ws://example.com/socket'); // Use WSS (WebSocket Secure) encrypted connection const secureWs = new WebSocket('wss://example.com/socket');

Listening to Connection Events

javascript
const ws = new WebSocket('ws://example.com/socket'); // Connection successful ws.onopen = (event) => { console.log('WebSocket connection established'); console.log('Connection state:', ws.readyState); // 1: OPEN }; // Receive messages ws.onmessage = (event) => { console.log('Received message:', event.data); // Handle different types of data if (typeof event.data === 'string') { // Text data const data = JSON.parse(event.data); handleMessage(data); } else if (event.data instanceof Blob) { // Binary data (Blob) event.data.text().then(text => { console.log('Blob data:', text); }); } else if (event.data instanceof ArrayBuffer) { // Binary data (ArrayBuffer) const view = new Uint8Array(event.data); console.log('ArrayBuffer data:', view); } }; // Connection closed ws.onclose = (event) => { console.log('WebSocket connection closed'); console.log('Close code:', event.code); console.log('Close reason:', event.reason); console.log('Clean close:', event.wasClean); }; // Connection error ws.onerror = (error) => { console.error('WebSocket error:', error); };

Sending Messages

javascript
// Send text message ws.send('Hello, WebSocket!'); // Send JSON data ws.send(JSON.stringify({ type: 'chat', message: 'Hello', userId: 123 })); // Send binary data const binaryData = new Uint8Array([1, 2, 3, 4, 5]); ws.send(binaryData); // Send Blob data const blob = new Blob(['Hello'], { type: 'text/plain' }); ws.send(blob); // Send ArrayBuffer const arrayBuffer = new ArrayBuffer(10); ws.send(arrayBuffer);

Connection State Management

WebSocket Ready States

javascript
const ws = new WebSocket('ws://example.com/socket'); // 0: CONNECTING - Connecting // 1: OPEN - Connected, can communicate // 2: CLOSING - Closing // 3: CLOSED - Closed function getConnectionState(ws) { switch (ws.readyState) { case WebSocket.CONNECTING: return 'Connecting'; case WebSocket.OPEN: return 'Connected'; case WebSocket.CLOSING: return 'Closing'; case WebSocket.CLOSED: return 'Closed'; default: return 'Unknown state'; } } // Check if connection is ready function isConnectionReady(ws) { return ws.readyState === WebSocket.OPEN; } // Safely send message function safeSend(ws, message) { if (isConnectionReady(ws)) { ws.send(message); } else { console.error('WebSocket not connected, cannot send message'); // Can add message to queue, send after connection recovers } }

Actively Close Connection

javascript
// Normal close connection ws.close(1000, 'Normal closure'); // Close status code explanation: // 1000: Normal closure // 1001: Endpoint going away (server closed or browser navigation) // 1002: Protocol error // 1003: Unsupported data type // 1008: Policy violation // 1009: Message too large // 1011: Internal error // Check if connection is closed if (ws.readyState === WebSocket.CLOSED) { console.log('Connection closed'); }

Encapsulating WebSocket Class

javascript
class WebSocketClient { constructor(url, options = {}) { this.url = url; this.options = { reconnect: true, reconnectInterval: 3000, maxReconnectAttempts: 5, heartbeatInterval: 30000, ...options }; this.ws = null; this.reconnectAttempts = 0; this.messageQueue = []; this.heartbeatTimer = null; this.eventHandlers = new Map(); this.connect(); } connect() { this.ws = new WebSocket(this.url); this.ws.onopen = () => { console.log('WebSocket connection established'); this.reconnectAttempts = 0; this.startHeartbeat(); this.flushMessageQueue(); this.emit('open'); }; this.ws.onmessage = (event) => { this.handleMessage(event.data); this.emit('message', event.data); }; this.ws.onclose = (event) => { console.log('WebSocket connection closed:', event.code); this.stopHeartbeat(); this.emit('close', event); if (this.options.reconnect && event.code !== 1000) { this.reconnect(); } }; this.ws.onerror = (error) => { console.error('WebSocket error:', error); this.emit('error', error); }; } send(data) { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.ws.send(data); } else { this.messageQueue.push(data); } } handleMessage(data) { try { const message = JSON.parse(data); // Handle heartbeat response if (message.type === 'pong') { return; } // Handle other messages this.emit('data', message); } catch (error) { console.error('Failed to parse message:', error); } } startHeartbeat() { this.heartbeatTimer = setInterval(() => { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.send(JSON.stringify({ type: 'ping' })); } }, this.options.heartbeatInterval); } stopHeartbeat() { if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer = null; } } reconnect() { if (this.reconnectAttempts >= this.options.maxReconnectAttempts) { console.error('Max reconnection attempts reached'); return; } this.reconnectAttempts++; const delay = this.getReconnectDelay(); console.log(`Attempting ${this.reconnectAttempts}th reconnection after ${delay}ms`); setTimeout(() => { this.connect(); }, delay); } getReconnectDelay() { return Math.min( this.options.reconnectInterval * Math.pow(2, this.reconnectAttempts - 1), 30000 ); } flushMessageQueue() { while (this.messageQueue.length > 0) { const message = this.messageQueue.shift(); this.send(message); } } close(code = 1000, reason = 'Normal closure') { this.stopHeartbeat(); if (this.ws) { this.ws.close(code, reason); } } on(event, handler) { if (!this.eventHandlers.has(event)) { this.eventHandlers.set(event, []); } this.eventHandlers.get(event).push(handler); } off(event, handler) { const handlers = this.eventHandlers.get(event); if (handlers) { const index = handlers.indexOf(handler); if (index !== -1) { handlers.splice(index, 1); } } } emit(event, ...args) { const handlers = this.eventHandlers.get(event); if (handlers) { handlers.forEach(handler => handler(...args)); } } } // Usage example const wsClient = new WebSocketClient('ws://example.com/socket', { reconnect: true, reconnectInterval: 3000, maxReconnectAttempts: 5 }); wsClient.on('open', () => { console.log('Connection opened'); wsClient.send(JSON.stringify({ type: 'login', userId: 123 })); }); wsClient.on('message', (data) => { console.log('Received message:', data); }); wsClient.on('data', (message) => { console.log('Handle message:', message); }); wsClient.on('close', (event) => { console.log('Connection closed:', event.code); }); wsClient.on('error', (error) => { console.error('Error occurred:', error); });

Practical Application Examples

Real-time Chat Application

javascript
class ChatClient { constructor(url, userId) { this.userId = userId; this.wsClient = new WebSocketClient(url); this.setupEventHandlers(); } setupEventHandlers() { this.wsClient.on('open', () => { this.login(); }); this.wsClient.on('data', (message) => { this.handleMessage(message); }); } login() { this.wsClient.send(JSON.stringify({ type: 'login', userId: this.userId })); } sendMessage(content) { this.wsClient.send(JSON.stringify({ type: 'chat', userId: this.userId, content: content, timestamp: Date.now() })); } handleMessage(message) { switch (message.type) { case 'chat': this.displayMessage(message); break; case 'user_joined': this.notifyUserJoined(message.userId); break; case 'user_left': this.notifyUserLeft(message.userId); break; } } displayMessage(message) { console.log(`${message.userId}: ${message.content}`); } notifyUserJoined(userId) { console.log(`User ${userId} joined the chat`); } notifyUserLeft(userId) { console.log(`User ${userId} left the chat`); } } // Usage example const chatClient = new ChatClient('ws://example.com/chat', 123); chatClient.sendMessage('Hello everyone!');

Real-time Data Updates

javascript
class DataStreamClient { constructor(url) { this.wsClient = new WebSocketClient(url); this.dataHandlers = new Map(); this.setupEventHandlers(); } setupEventHandlers() { this.wsClient.on('data', (message) => { this.handleData(message); }); } subscribe(dataType, handler) { this.dataHandlers.set(dataType, handler); this.wsClient.send(JSON.stringify({ type: 'subscribe', dataType: dataType })); } unsubscribe(dataType) { this.dataHandlers.delete(dataType); this.wsClient.send(JSON.stringify({ type: 'unsubscribe', dataType: dataType })); } handleData(message) { const handler = this.dataHandlers.get(message.dataType); if (handler) { handler(message.data); } } } // Usage example const dataClient = new DataStreamClient('ws://example.com/stream'); dataClient.subscribe('stock_price', (data) => { console.log('Stock price update:', data); }); dataClient.subscribe('weather', (data) => { console.log('Weather update:', data); });

Browser Compatibility

Detect Browser Support

javascript
function isWebSocketSupported() { return 'WebSocket' in window || 'MozWebSocket' in window; } if (isWebSocketSupported()) { console.log('Browser supports WebSocket'); } else { console.log('Browser does not support WebSocket'); // Use fallback solution, such as long polling }

Fallback Solution

javascript
class RealtimeClient { constructor(url) { this.url = url; if (isWebSocketSupported()) { this.client = new WebSocketClient(url); } else { this.client = new LongPollingClient(url); } } send(data) { this.client.send(data); } on(event, handler) { this.client.on(event, handler); } }

Best Practices

  1. Use WSS: Always use encrypted connections in production
  2. Error Handling: Properly handle all possible errors
  3. Reconnection Mechanism: Implement automatic reconnection
  4. Heartbeat Detection: Periodically check connection status
  5. Message Queue: Cache messages when offline
  6. State Management: Clearly manage connection state
  7. Resource Cleanup: Clean up timers and event listeners in time
  8. Fallback Solution: Provide alternative for browsers that don't support WebSocket
标签:WebSocket