Search K
Appearance
Appearance
📊 SEO元描述:2024年最新WebSocket API详解教程,深入讲解连接建立、消息发送接收、事件处理机制。包含完整代码示例,适合前端开发者掌握WebSocket编程技术。
核心关键词:WebSocket API教程2024、JavaScript WebSocket编程、WebSocket连接建立、消息发送接收、前端实时通信
长尾关键词:WebSocket API怎么使用、JavaScript WebSocket教程、WebSocket消息处理、前端实时编程、WebSocket事件监听
通过本节WebSocket API详解,你将系统性掌握:
WebSocket API是什么?这是浏览器提供的原生JavaScript接口,用于创建和管理WebSocket连接。WebSocket API提供了简洁而强大的编程接口,让开发者能够轻松构建实时双向通信应用,也是现代Web实时编程的基础工具。
💡 学习建议:掌握WebSocket API是构建现代实时Web应用的必备技能,它提供了最直接和高效的实时通信解决方案。
WebSocket API的核心是WebSocket构造函数,用于创建WebSocket连接:
// 🎉 WebSocket对象创建和基本操作
class WebSocketManager {
constructor() {
this.connections = new Map();
this.defaultOptions = {
protocols: [],
binaryType: 'blob', // 'blob' 或 'arraybuffer'
reconnectAttempts: 3,
reconnectDelay: 1000
};
}
// 创建WebSocket连接
createConnection(url, options = {}) {
const config = { ...this.defaultOptions, ...options };
const connectionId = this.generateConnectionId();
try {
// 创建WebSocket实例
const ws = config.protocols.length > 0
? new WebSocket(url, config.protocols)
: new WebSocket(url);
// 设置二进制数据类型
ws.binaryType = config.binaryType;
// 保存连接信息
const connectionInfo = {
id: connectionId,
url,
websocket: ws,
state: 'connecting',
config,
createdAt: Date.now(),
lastActivity: Date.now(),
messageCount: 0,
errorCount: 0
};
this.connections.set(connectionId, connectionInfo);
// 设置事件监听器
this.setupEventListeners(connectionId);
console.log(`WebSocket连接创建: ${connectionId} -> ${url}`);
return connectionId;
} catch (error) {
console.error('WebSocket创建失败:', error);
throw error;
}
}
// 设置事件监听器
setupEventListeners(connectionId) {
const connection = this.connections.get(connectionId);
if (!connection) return;
const { websocket } = connection;
// 连接打开事件
websocket.onopen = (event) => {
connection.state = 'open';
connection.lastActivity = Date.now();
console.log(`✅ WebSocket连接已建立: ${connectionId}`);
this.handleConnectionOpen(connectionId, event);
};
// 消息接收事件
websocket.onmessage = (event) => {
connection.lastActivity = Date.now();
connection.messageCount++;
console.log(`📨 收到消息 [${connectionId}]:`, event.data);
this.handleMessage(connectionId, event);
};
// 连接关闭事件
websocket.onclose = (event) => {
connection.state = 'closed';
console.log(`🔌 WebSocket连接关闭: ${connectionId}`, {
code: event.code,
reason: event.reason,
wasClean: event.wasClean
});
this.handleConnectionClose(connectionId, event);
};
// 错误事件
websocket.onerror = (event) => {
connection.errorCount++;
console.error(`❌ WebSocket错误 [${connectionId}]:`, event);
this.handleError(connectionId, event);
};
}
// 发送消息
sendMessage(connectionId, data, type = 'text') {
const connection = this.connections.get(connectionId);
if (!connection) {
throw new Error(`连接不存在: ${connectionId}`);
}
const { websocket } = connection;
if (websocket.readyState !== WebSocket.OPEN) {
throw new Error(`连接未就绪: ${websocket.readyState}`);
}
try {
let messageData;
switch (type) {
case 'text':
messageData = typeof data === 'string' ? data : JSON.stringify(data);
break;
case 'json':
messageData = JSON.stringify(data);
break;
case 'binary':
messageData = data; // ArrayBuffer, Blob等
break;
default:
messageData = data;
}
websocket.send(messageData);
connection.lastActivity = Date.now();
console.log(`📤 发送消息 [${connectionId}] (${type}):`, data);
return true;
} catch (error) {
console.error(`发送消息失败 [${connectionId}]:`, error);
return false;
}
}
// 关闭连接
closeConnection(connectionId, code = 1000, reason = '') {
const connection = this.connections.get(connectionId);
if (!connection) return false;
const { websocket } = connection;
if (websocket.readyState === WebSocket.OPEN ||
websocket.readyState === WebSocket.CONNECTING) {
websocket.close(code, reason);
console.log(`🔌 主动关闭连接: ${connectionId}`);
return true;
}
return false;
}
// 获取连接状态
getConnectionState(connectionId) {
const connection = this.connections.get(connectionId);
if (!connection) return null;
const { websocket } = connection;
const readyStateNames = {
[WebSocket.CONNECTING]: 'CONNECTING',
[WebSocket.OPEN]: 'OPEN',
[WebSocket.CLOSING]: 'CLOSING',
[WebSocket.CLOSED]: 'CLOSED'
};
return {
id: connectionId,
url: connection.url,
state: connection.state,
readyState: readyStateNames[websocket.readyState],
readyStateCode: websocket.readyState,
protocol: websocket.protocol,
extensions: websocket.extensions,
binaryType: websocket.binaryType,
bufferedAmount: websocket.bufferedAmount,
messageCount: connection.messageCount,
errorCount: connection.errorCount,
uptime: Date.now() - connection.createdAt,
lastActivity: connection.lastActivity
};
}
// 事件处理方法(可以被重写)
handleConnectionOpen(connectionId, event) {
// 子类可以重写此方法
this.onConnectionOpen?.(connectionId, event);
}
handleMessage(connectionId, event) {
// 解析消息数据
let parsedData;
try {
if (typeof event.data === 'string') {
// 尝试解析JSON
parsedData = JSON.parse(event.data);
} else {
// 二进制数据
parsedData = event.data;
}
} catch (error) {
// 纯文本数据
parsedData = event.data;
}
// 子类可以重写此方法
this.onMessage?.(connectionId, parsedData, event);
}
handleConnectionClose(connectionId, event) {
// 根据关闭代码判断是否需要重连
if (event.code !== 1000 && event.code !== 1001) {
console.log(`连接异常关闭,考虑重连: ${connectionId}`);
this.attemptReconnect(connectionId);
}
// 子类可以重写此方法
this.onConnectionClose?.(connectionId, event);
}
handleError(connectionId, event) {
// 子类可以重写此方法
this.onError?.(connectionId, event);
}
// 重连机制
attemptReconnect(connectionId) {
const connection = this.connections.get(connectionId);
if (!connection) return;
const { config, url } = connection;
if (config.reconnectAttempts > 0) {
setTimeout(() => {
console.log(`尝试重连: ${connectionId}`);
config.reconnectAttempts--;
// 移除旧连接
this.connections.delete(connectionId);
// 创建新连接
try {
this.createConnection(url, config);
} catch (error) {
console.error('重连失败:', error);
}
}, config.reconnectDelay);
}
}
generateConnectionId() {
return `ws_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
// 获取所有连接状态
getAllConnections() {
const connections = [];
for (const [id] of this.connections) {
connections.push(this.getConnectionState(id));
}
return connections;
}
// 清理所有连接
cleanup() {
for (const [id] of this.connections) {
this.closeConnection(id);
}
this.connections.clear();
}
}
// 使用示例
const wsManager = new WebSocketManager();
// 创建连接
const connectionId = wsManager.createConnection('wss://echo.websocket.org', {
protocols: ['echo-protocol'],
binaryType: 'arraybuffer',
reconnectAttempts: 5,
reconnectDelay: 2000
});
// 设置事件回调
wsManager.onConnectionOpen = (id, event) => {
console.log('连接建立回调:', id);
// 发送测试消息
wsManager.sendMessage(id, { type: 'greeting', message: 'Hello WebSocket!' }, 'json');
};
wsManager.onMessage = (id, data, event) => {
console.log('消息接收回调:', id, data);
};
wsManager.onConnectionClose = (id, event) => {
console.log('连接关闭回调:', id, event.code, event.reason);
};
wsManager.onError = (id, event) => {
console.error('错误回调:', id, event);
};
// 定期检查连接状态
setInterval(() => {
const state = wsManager.getConnectionState(connectionId);
if (state) {
console.log('连接状态:', state);
}
}, 5000);消息传输是WebSocket的核心功能,支持文本和二进制数据的双向传输:
// 消息处理的完整实现
class MessageHandler {
constructor(websocket) {
this.websocket = websocket;
this.messageQueue = [];
this.messageHandlers = new Map();
this.setupMessageProcessing();
}
setupMessageProcessing() {
this.websocket.onmessage = (event) => {
this.processIncomingMessage(event);
};
}
// 处理接收到的消息
processIncomingMessage(event) {
const message = {
timestamp: Date.now(),
raw: event.data,
type: this.detectMessageType(event.data),
processed: null
};
try {
// 根据类型处理消息
switch (message.type) {
case 'text':
message.processed = event.data;
break;
case 'json':
message.processed = JSON.parse(event.data);
break;
case 'binary':
message.processed = this.processBinaryData(event.data);
break;
default:
message.processed = event.data;
}
// 添加到消息队列
this.messageQueue.push(message);
// 触发相应的处理器
this.triggerMessageHandlers(message);
} catch (error) {
console.error('消息处理失败:', error, event.data);
this.handleMessageError(message, error);
}
}
detectMessageType(data) {
if (typeof data === 'string') {
try {
JSON.parse(data);
return 'json';
} catch {
return 'text';
}
} else {
return 'binary';
}
}
processBinaryData(data) {
if (data instanceof ArrayBuffer) {
return {
type: 'ArrayBuffer',
byteLength: data.byteLength,
data: new Uint8Array(data)
};
} else if (data instanceof Blob) {
return {
type: 'Blob',
size: data.size,
mimeType: data.type,
data: data
};
}
return data;
}
// 发送不同类型的消息
sendTextMessage(text) {
if (this.websocket.readyState === WebSocket.OPEN) {
this.websocket.send(text);
return true;
}
return false;
}
sendJSONMessage(object) {
try {
const jsonString = JSON.stringify(object);
return this.sendTextMessage(jsonString);
} catch (error) {
console.error('JSON序列化失败:', error);
return false;
}
}
sendBinaryMessage(data) {
if (this.websocket.readyState === WebSocket.OPEN) {
this.websocket.send(data);
return true;
}
return false;
}
// 发送文件
async sendFile(file) {
if (!(file instanceof File)) {
throw new Error('参数必须是File对象');
}
// 发送文件信息
const fileInfo = {
type: 'file_info',
name: file.name,
size: file.size,
mimeType: file.type,
lastModified: file.lastModified
};
this.sendJSONMessage(fileInfo);
// 发送文件数据
const arrayBuffer = await file.arrayBuffer();
return this.sendBinaryMessage(arrayBuffer);
}
// 注册消息处理器
onMessage(type, handler) {
if (!this.messageHandlers.has(type)) {
this.messageHandlers.set(type, []);
}
this.messageHandlers.get(type).push(handler);
}
// 触发消息处理器
triggerMessageHandlers(message) {
const handlers = this.messageHandlers.get(message.type) || [];
handlers.forEach(handler => {
try {
handler(message.processed, message);
} catch (error) {
console.error('消息处理器执行失败:', error);
}
});
// 触发通用处理器
const allHandlers = this.messageHandlers.get('*') || [];
allHandlers.forEach(handler => {
try {
handler(message.processed, message);
} catch (error) {
console.error('通用消息处理器执行失败:', error);
}
});
}
handleMessageError(message, error) {
const errorHandlers = this.messageHandlers.get('error') || [];
errorHandlers.forEach(handler => {
try {
handler(error, message);
} catch (handlerError) {
console.error('错误处理器执行失败:', handlerError);
}
});
}
// 获取消息历史
getMessageHistory(limit = 100) {
return this.messageQueue.slice(-limit);
}
// 清理消息队列
clearMessageHistory() {
this.messageQueue = [];
}
}
// 使用示例
const ws = new WebSocket('wss://echo.websocket.org');
const messageHandler = new MessageHandler(ws);
// 注册消息处理器
messageHandler.onMessage('json', (data, message) => {
console.log('收到JSON消息:', data);
// 处理不同类型的JSON消息
switch (data.type) {
case 'chat':
console.log('聊天消息:', data.message);
break;
case 'notification':
console.log('通知:', data.content);
break;
default:
console.log('未知JSON消息类型:', data.type);
}
});
messageHandler.onMessage('text', (data, message) => {
console.log('收到文本消息:', data);
});
messageHandler.onMessage('binary', (data, message) => {
console.log('收到二进制消息:', data);
});
messageHandler.onMessage('error', (error, message) => {
console.error('消息处理错误:', error);
});
// 连接建立后发送测试消息
ws.onopen = () => {
// 发送文本消息
messageHandler.sendTextMessage('Hello WebSocket!');
// 发送JSON消息
messageHandler.sendJSONMessage({
type: 'chat',
user: 'Alice',
message: 'Hello everyone!',
timestamp: Date.now()
});
// 发送二进制数据
const binaryData = new ArrayBuffer(8);
const view = new DataView(binaryData);
view.setUint32(0, 12345678);
messageHandler.sendBinaryMessage(binaryData);
};消息处理的核心要点:
💼 实际开发经验:在实际项目中,建议定义统一的消息格式和协议,包含消息类型、时间戳、数据载荷等字段,便于消息的路由和处理。
通过本节WebSocket API详解的学习,你已经掌握:
A: 浏览器通常限制每个域名的WebSocket连接数(一般是255个)。服务器端的限制取决于服务器配置和资源。
A: 实现心跳检测机制,定期发送ping消息,如果在指定时间内没有收到pong响应,则认为连接已断开。
A: 理论上没有限制,但实际受到浏览器和服务器的内存限制。建议大文件分块传输。
A: 监控bufferedAmount属性,当缓冲区满时暂停发送,等待缓冲区清空后继续发送。
A: 支持。可以通过WebSocket扩展(如permessage-deflate)实现消息压缩,减少网络传输量。
// 问题:消息发送时连接状态不正确
// 解决:检查连接状态并实现消息队列
class SafeWebSocketSender {
constructor(websocket) {
this.websocket = websocket;
this.messageQueue = [];
this.isProcessingQueue = false;
}
send(data) {
if (this.websocket.readyState === WebSocket.OPEN) {
this.websocket.send(data);
return true;
} else {
// 连接未就绪,加入队列
this.messageQueue.push(data);
this.processQueueWhenReady();
return false;
}
}
processQueueWhenReady() {
if (this.isProcessingQueue) return;
this.isProcessingQueue = true;
const checkAndSend = () => {
if (this.websocket.readyState === WebSocket.OPEN && this.messageQueue.length > 0) {
const message = this.messageQueue.shift();
this.websocket.send(message);
setTimeout(checkAndSend, 10); // 避免阻塞
} else if (this.messageQueue.length > 0) {
setTimeout(checkAndSend, 100); // 等待连接就绪
} else {
this.isProcessingQueue = false;
}
};
checkAndSend();
}
}// 问题:WebSocket对象和事件监听器未正确清理
// 解决:实现完整的资源清理机制
class WebSocketWithCleanup {
constructor(url) {
this.websocket = new WebSocket(url);
this.eventListeners = [];
this.isDestroyed = false;
}
addEventListener(type, listener) {
this.websocket.addEventListener(type, listener);
this.eventListeners.push({ type, listener });
}
destroy() {
if (this.isDestroyed) return;
// 移除所有事件监听器
this.eventListeners.forEach(({ type, listener }) => {
this.websocket.removeEventListener(type, listener);
});
// 关闭连接
if (this.websocket.readyState === WebSocket.OPEN) {
this.websocket.close();
}
this.isDestroyed = true;
this.eventListeners = [];
}
}"掌握WebSocket API让你能够构建真正的实时交互应用。继续学习实际应用场景和高级特性,你将成为实时Web开发的专家!"