WebSocket API是HTML5的一部分,所有现代浏览器都支持WebSocket。
基础用法
创建WebSocket连接
javascript// 创建WebSocket连接 const ws = new WebSocket('ws://example.com/socket'); // 使用WSS(WebSocket Secure)加密连接 const secureWs = new WebSocket('wss://example.com/socket');
监听连接事件
javascriptconst ws = new WebSocket('ws://example.com/socket'); // 连接成功 ws.onopen = (event) => { console.log('WebSocket连接已建立'); console.log('连接状态:', ws.readyState); // 1: OPEN }; // 接收消息 ws.onmessage = (event) => { console.log('收到消息:', event.data); // 处理不同类型的数据 if (typeof event.data === 'string') { // 文本数据 const data = JSON.parse(event.data); handleMessage(data); } else if (event.data instanceof Blob) { // 二进制数据(Blob) event.data.text().then(text => { console.log('Blob数据:', text); }); } else if (event.data instanceof ArrayBuffer) { // 二进制数据(ArrayBuffer) const view = new Uint8Array(event.data); console.log('ArrayBuffer数据:', view); } }; // 连接关闭 ws.onclose = (event) => { console.log('WebSocket连接已关闭'); console.log('关闭码:', event.code); console.log('关闭原因:', event.reason); console.log('是否干净关闭:', event.wasClean); }; // 连接错误 ws.onerror = (error) => { console.error('WebSocket错误:', error); };
发送消息
javascript// 发送文本消息 ws.send('Hello, WebSocket!'); // 发送JSON数据 ws.send(JSON.stringify({ type: 'chat', message: '你好', userId: 123 })); // 发送二进制数据 const binaryData = new Uint8Array([1, 2, 3, 4, 5]); ws.send(binaryData); // 发送Blob数据 const blob = new Blob(['Hello'], { type: 'text/plain' }); ws.send(blob); // 发送ArrayBuffer const arrayBuffer = new ArrayBuffer(10); ws.send(arrayBuffer);
连接状态管理
WebSocket就绪状态
javascriptconst ws = new WebSocket('ws://example.com/socket'); // 0: CONNECTING - 正在连接 // 1: OPEN - 已连接,可以通信 // 2: CLOSING - 正在关闭 // 3: CLOSED - 已关闭 function getConnectionState(ws) { switch (ws.readyState) { case WebSocket.CONNECTING: return '正在连接'; case WebSocket.OPEN: return '已连接'; case WebSocket.CLOSING: return '正在关闭'; case WebSocket.CLOSED: return '已关闭'; default: return '未知状态'; } } // 检查连接是否可用 function isConnectionReady(ws) { return ws.readyState === WebSocket.OPEN; } // 安全发送消息 function safeSend(ws, message) { if (isConnectionReady(ws)) { ws.send(message); } else { console.error('WebSocket未连接,无法发送消息'); // 可以将消息加入队列,等待连接恢复后发送 } }
主动关闭连接
javascript// 正常关闭连接 ws.close(1000, '正常关闭'); // 关闭状态码说明: // 1000: 正常关闭 // 1001: 端点离开(服务器关闭或浏览器导航) // 1002: 协议错误 // 1003: 不支持的数据类型 // 1008: 策略违规 // 1009: 消息过大 // 1011: 内部错误 // 检查连接是否已关闭 if (ws.readyState === WebSocket.CLOSED) { console.log('连接已关闭'); }
封装WebSocket类
javascriptclass 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连接已建立'); 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连接已关闭:', 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); 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); // 处理心跳响应 if (message.type === 'pong') { return; } // 处理其他消息 this.emit('data', message); } catch (error) { console.error('解析消息失败:', 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('达到最大重连次数'); return; } this.reconnectAttempts++; const delay = this.getReconnectDelay(); console.log(`${delay}ms后尝试第${this.reconnectAttempts}次重连`); 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)); } } } // 使用示例 const wsClient = new WebSocketClient('ws://example.com/socket', { reconnect: true, reconnectInterval: 3000, maxReconnectAttempts: 5 }); wsClient.on('open', () => { console.log('连接已打开'); wsClient.send(JSON.stringify({ type: 'login', userId: 123 })); }); wsClient.on('message', (data) => { console.log('收到消息:', data); }); wsClient.on('data', (message) => { console.log('处理消息:', message); }); wsClient.on('close', (event) => { console.log('连接已关闭:', event.code); }); wsClient.on('error', (error) => { console.error('发生错误:', error); });
实际应用示例
实时聊天应用
javascriptclass 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(`用户 ${userId} 加入了聊天`); } notifyUserLeft(userId) { console.log(`用户 ${userId} 离开了聊天`); } } // 使用示例 const chatClient = new ChatClient('ws://example.com/chat', 123); chatClient.sendMessage('大家好!');
实时数据更新
javascriptclass 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); } } } // 使用示例 const dataClient = new DataStreamClient('ws://example.com/stream'); dataClient.subscribe('stock_price', (data) => { console.log('股票价格更新:', data); }); dataClient.subscribe('weather', (data) => { console.log('天气更新:', data); });
浏览器兼容性
检测浏览器支持
javascriptfunction isWebSocketSupported() { return 'WebSocket' in window || 'MozWebSocket' in window; } if (isWebSocketSupported()) { console.log('浏览器支持WebSocket'); } else { console.log('浏览器不支持WebSocket'); // 使用降级方案,如长轮询 }
降级方案
javascriptclass 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); } }
最佳实践
- 使用WSS:生产环境始终使用加密连接
- 错误处理:妥善处理所有可能的错误
- 重连机制:实现自动重连功能
- 心跳检测:定期检测连接状态
- 消息队列:离线时缓存消息
- 状态管理:清晰管理连接状态
- 资源清理:及时清理定时器和事件监听器
- 降级方案:为不支持WebSocket的浏览器提供替代方案