WebSocket 基础 #
WebSocket API 概览 #
浏览器提供了原生的 WebSocket API,通过简单的 JavaScript 代码即可实现实时通信。
text
┌─────────────────────────────────────────────────────────────┐
│ WebSocket API 结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ WebSocket 构造函数 │
│ ───────────────────────────────────────────────────────── │
│ new WebSocket(url, protocols) │
│ │
│ 属性 │
│ ───────────────────────────────────────────────────────── │
│ readyState 连接状态 │
│ url 连接地址 │
│ protocol 使用的协议 │
│ bufferedAmount 待发送数据量 │
│ │
│ 事件 │
│ ───────────────────────────────────────────────────────── │
│ onopen 连接打开 │
│ onmessage 收到消息 │
│ onerror 发生错误 │
│ onclose 连接关闭 │
│ │
│ 方法 │
│ ───────────────────────────────────────────────────────── │
│ send() 发送消息 │
│ close() 关闭连接 │
│ │
└─────────────────────────────────────────────────────────────┘
创建 WebSocket 连接 #
基本连接 #
javascript
const socket = new WebSocket('wss://example.com/ws');
socket.onopen = function(event) {
console.log('WebSocket 连接已建立');
};
socket.onmessage = function(event) {
console.log('收到消息:', event.data);
};
socket.onerror = function(error) {
console.error('WebSocket 错误:', error);
};
socket.onclose = function(event) {
console.log('WebSocket 连接已关闭');
};
连接 URL #
text
┌─────────────────────────────────────────────────────────────┐
│ WebSocket URL 格式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 非加密连接: │
│ ws://host:port/path?query │
│ │
│ 加密连接(推荐): │
│ wss://host:port/path?query │
│ │
│ 示例: │
│ ws://localhost:8080/ws │
│ wss://api.example.com/socket │
│ wss://example.com/ws?token=abc123 │
│ │
│ 注意: │
│ - wss 是 ws 的安全版本(类似 https) │
│ - 生产环境必须使用 wss │
│ - 可以在 URL 中传递参数 │
│ │
└─────────────────────────────────────────────────────────────┘
指定子协议 #
javascript
const socket = new WebSocket('wss://example.com/ws', ['chat', 'json']);
socket.onopen = function(event) {
console.log('选择的协议:', socket.protocol);
};
text
┌─────────────────────────────────────────────────────────────┐
│ 子协议说明 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 子协议用于约定数据格式和通信规则: │
│ │
│ 常见子协议: │
│ ───────────────────────────────────────────────────────── │
│ chat 聊天协议 │
│ json JSON 数据格式 │
│ soap SOAP 协议 │
│ mqtt MQTT 协议 │
│ │
│ 工作流程: │
│ 1. 客户端发送支持的协议列表 │
│ 2. 服务端选择一个协议 │
│ 3. 连接建立后使用选定的协议 │
│ │
│ 如果服务端不接受任何协议,连接会被拒绝 │
│ │
└─────────────────────────────────────────────────────────────┘
连接状态 #
readyState 属性 #
javascript
const socket = new WebSocket('wss://example.com/ws');
console.log(socket.readyState);
text
┌─────────────────────────────────────────────────────────────┐
│ readyState 状态值 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 值 常量 说明 │
│ ───────────────────────────────────────────────────────── │
│ 0 CONNECTING 正在连接 │
│ 1 OPEN 连接已建立 │
│ 2 CLOSING 正在关闭 │
│ 3 CLOSED 连接已关闭 │
│ │
│ 状态转换: │
│ │
│ CONNECTING ──► OPEN ──► CLOSING ──► CLOSED │
│ │ │ │ │
│ │ │ └── close() 调用 │
│ │ └── onopen 触发 │
│ └── new WebSocket() │
│ │
└─────────────────────────────────────────────────────────────┘
状态检查 #
javascript
function checkConnection(socket) {
switch (socket.readyState) {
case WebSocket.CONNECTING:
console.log('正在连接...');
break;
case WebSocket.OPEN:
console.log('连接已建立');
break;
case WebSocket.CLOSING:
console.log('正在关闭...');
break;
case WebSocket.CLOSED:
console.log('连接已关闭');
break;
}
}
发送消息 #
发送文本消息 #
javascript
const socket = new WebSocket('wss://example.com/ws');
socket.onopen = function() {
socket.send('Hello, WebSocket!');
socket.send(JSON.stringify({
type: 'message',
content: 'Hello',
timestamp: Date.now()
}));
};
发送二进制数据 #
javascript
socket.onopen = function() {
const buffer = new ArrayBuffer(1024);
const view = new Uint8Array(buffer);
for (let i = 0; i < 1024; i++) {
view[i] = i % 256;
}
socket.send(buffer);
};
socket.onopen = function() {
const blob = new Blob(['Hello', ' ', 'World'], { type: 'text/plain' });
socket.send(blob);
};
检查发送队列 #
javascript
function sendMessage(socket, data) {
if (socket.readyState === WebSocket.OPEN) {
socket.send(data);
if (socket.bufferedAmount > 0) {
console.log('数据正在排队等待发送');
}
} else {
console.log('连接未打开,无法发送');
}
}
text
┌─────────────────────────────────────────────────────────────┐
│ bufferedAmount 属性 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 表示已排队但尚未发送到网络的数据字节数 │
│ │
│ 用途: │
│ ───────────────────────────────────────────────────────── │
│ 1. 流量控制 │
│ 避免发送过多数据导致内存溢出 │
│ │
│ 2. 上传进度 │
│ 监控数据发送进度 │
│ │
│ 3. 背压处理 │
│ 当网络拥塞时暂停发送 │
│ │
│ 示例: │
│ function sendLargeData(socket, data) { │
│ if (socket.bufferedAmount < 16384) { │
│ socket.send(data); │
│ } else { │
│ setTimeout(() => sendLargeData(socket, data), 100); │
│ } │
│ } │
│ │
└─────────────────────────────────────────────────────────────┘
接收消息 #
处理文本消息 #
javascript
socket.onmessage = function(event) {
console.log('收到消息:', event.data);
try {
const message = JSON.parse(event.data);
console.log('解析后的消息:', message);
} catch (e) {
console.log('非 JSON 消息:', event.data);
}
};
处理二进制消息 #
javascript
socket.binaryType = 'blob';
socket.onmessage = function(event) {
if (event.data instanceof Blob) {
console.log('收到 Blob 数据:', event.data.size, 'bytes');
const reader = new FileReader();
reader.onload = function() {
console.log('Blob 内容:', reader.result);
};
reader.readAsText(event.data);
}
};
javascript
socket.binaryType = 'arraybuffer';
socket.onmessage = function(event) {
if (event.data instanceof ArrayBuffer) {
const view = new DataView(event.data);
console.log('收到 ArrayBuffer:', event.data.byteLength, 'bytes');
}
};
text
┌─────────────────────────────────────────────────────────────┐
│ binaryType 设置 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 可选值: │
│ ───────────────────────────────────────────────────────── │
│ 'blob' 接收 Blob 对象(默认) │
│ 'arraybuffer' 接收 ArrayBuffer 对象 │
│ │
│ 选择建议: │
│ ───────────────────────────────────────────────────────── │
│ Blob 文件传输、图片处理 │
│ ArrayBuffer 高性能场景、游戏、音视频 │
│ │
│ 示例对比: │
│ socket.binaryType = 'blob'; │
│ // event.data 是 Blob 对象 │
│ │
│ socket.binaryType = 'arraybuffer'; │
│ // event.data 是 ArrayBuffer 对象 │
│ │
└─────────────────────────────────────────────────────────────┘
事件处理 #
使用事件监听器 #
javascript
const socket = new WebSocket('wss://example.com/ws');
socket.addEventListener('open', function(event) {
console.log('连接已打开');
});
socket.addEventListener('message', function(event) {
console.log('收到消息:', event.data);
});
socket.addEventListener('error', function(event) {
console.error('发生错误:', event);
});
socket.addEventListener('close', function(event) {
console.log('连接已关闭');
});
onopen 事件 #
javascript
socket.onopen = function(event) {
console.log('WebSocket 连接已建立');
console.log('URL:', event.target.url);
socket.send('连接成功!');
};
text
┌─────────────────────────────────────────────────────────────┐
│ onopen 事件详情 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 触发时机: │
│ 连接成功建立后立即触发 │
│ │
│ 事件对象: │
│ ───────────────────────────────────────────────────────── │
│ event.target WebSocket 实例 │
│ event.type 'open' │
│ │
│ 常见操作: │
│ ───────────────────────────────────────────────────────── │
│ - 发送初始消息 │
│ - 开始心跳检测 │
│ - 更新 UI 状态 │
│ - 订阅频道 │
│ │
└─────────────────────────────────────────────────────────────┘
onmessage 事件 #
javascript
socket.onmessage = function(event) {
console.log('消息来源:', event.target.url);
console.log('消息数据:', event.data);
console.log('数据类型:', typeof event.data);
console.log('数据来源:', event.origin);
};
text
┌─────────────────────────────────────────────────────────────┐
│ onmessage 事件详情 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 触发时机: │
│ 收到服务端发送的消息时触发 │
│ │
│ 事件对象: │
│ ───────────────────────────────────────────────────────── │
│ event.data 消息内容(字符串或二进制) │
│ event.origin 消息来源(仅用于跨域场景) │
│ event.target WebSocket 实例 │
│ event.type 'message' │
│ │
│ 数据类型: │
│ ───────────────────────────────────────────────────────── │
│ 字符串 JSON、普通文本 │
│ Blob 二进制数据(binaryType = 'blob') │
│ ArrayBuffer 二进制数据(binaryType = 'arraybuffer') │
│ │
└─────────────────────────────────────────────────────────────┘
onerror 事件 #
javascript
socket.onerror = function(event) {
console.error('WebSocket 错误:', event);
console.log('错误类型:', event.type);
};
text
┌─────────────────────────────────────────────────────────────┐
│ onerror 事件详情 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 触发时机: │
│ 连接或通信过程中发生错误 │
│ │
│ 常见错误原因: │
│ ───────────────────────────────────────────────────────── │
│ - 连接 URL 无效 │
│ - 服务器拒绝连接 │
│ - 网络中断 │
│ - 数据格式错误 │
│ - 服务器内部错误 │
│ │
│ 注意: │
│ ───────────────────────────────────────────────────────── │
│ ⚠️ error 事件不包含具体错误信息 │
│ ⚠️ 需要结合 close 事件判断具体原因 │
│ ⚠️ 通常在 error 后会触发 close 事件 │
│ │
└─────────────────────────────────────────────────────────────┘
onclose 事件 #
javascript
socket.onclose = function(event) {
console.log('连接已关闭');
console.log('关闭码:', event.code);
console.log('关闭原因:', event.reason);
console.log('是否正常关闭:', event.wasClean);
};
text
┌─────────────────────────────────────────────────────────────┐
│ onclose 事件详情 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 触发时机: │
│ WebSocket 连接关闭时触发 │
│ │
│ 事件对象: │
│ ───────────────────────────────────────────────────────── │
│ event.code 关闭状态码 │
│ event.reason 关闭原因描述 │
│ event.wasClean 是否正常关闭 │
│ event.target WebSocket 实例 │
│ │
│ 常见操作: │
│ ───────────────────────────────────────────────────────── │
│ - 更新 UI 状态 │
│ - 记录日志 │
│ - 触发重连逻辑 │
│ - 清理资源 │
│ │
└─────────────────────────────────────────────────────────────┘
关闭连接 #
基本关闭 #
javascript
socket.close();
指定关闭码和原因 #
javascript
socket.close(1000, '正常关闭');
socket.close(1001, '客户端离开');
socket.close(1003, '不支持的数据类型');
text
┌─────────────────────────────────────────────────────────────┐
│ WebSocket 关闭码 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 常用关闭码: │
│ ───────────────────────────────────────────────────────── │
│ 1000 正常关闭 │
│ 1001 端点离开(服务器关闭或浏览器离开页面) │
│ 1002 协议错误 │
│ 1003 不支持的数据类型 │
│ 1005 无状态码(保留) │
│ 1006 异常关闭(无关闭帧) │
│ 1007 无效数据 │
│ 1008 策略违规 │
│ 1009 消息过大 │
│ 1010 缺少扩展 │
│ 1011 内部错误 │
│ 1012 服务重启 │
│ 1013 稍后重试 │
│ 1015 TLS 握手失败 │
│ │
│ 自定义关闭码: │
│ ───────────────────────────────────────────────────────── │
│ 3000-3999 保留给库、框架使用 │
│ 4000-4999 保留给应用使用 │
│ │
└─────────────────────────────────────────────────────────────┘
优雅关闭 #
javascript
function closeSocket(socket, reason) {
if (socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify({
type: 'bye',
reason: reason
}));
socket.close(1000, reason);
}
}
完整示例 #
简单聊天客户端 #
javascript
class ChatClient {
constructor(url) {
this.url = url;
this.socket = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
}
connect() {
this.socket = new WebSocket(this.url);
this.socket.onopen = () => {
console.log('已连接到聊天服务器');
this.reconnectAttempts = 0;
this.onConnect();
};
this.socket.onmessage = (event) => {
const message = JSON.parse(event.data);
this.onMessage(message);
};
this.socket.onerror = (error) => {
console.error('连接错误:', error);
};
this.socket.onclose = (event) => {
console.log('连接关闭:', event.code, event.reason);
this.onDisconnect(event);
if (event.code !== 1000 && this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnect();
}
};
}
send(message) {
if (this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify(message));
}
}
reconnect() {
this.reconnectAttempts++;
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
console.log(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
setTimeout(() => {
this.connect();
}, delay);
}
close() {
if (this.socket) {
this.socket.close(1000, '客户端主动关闭');
}
}
onConnect() {}
onMessage(message) {}
onDisconnect(event) {}
}
const client = new ChatClient('wss://example.com/chat');
client.onConnect = function() {
this.send({ type: 'join', room: 'general' });
};
client.onMessage = function(message) {
console.log(`[${message.user}]: ${message.text}`);
};
client.connect();
实时数据订阅 #
javascript
class DataSubscriber {
constructor(url) {
this.url = url;
this.socket = null;
this.subscriptions = new Set();
}
connect() {
this.socket = new WebSocket(this.url);
this.socket.onopen = () => {
this.subscriptions.forEach(channel => {
this.socket.send(JSON.stringify({
action: 'subscribe',
channel: channel
}));
});
};
this.socket.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleData(data);
};
}
subscribe(channel) {
this.subscriptions.add(channel);
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify({
action: 'subscribe',
channel: channel
}));
}
}
unsubscribe(channel) {
this.subscriptions.delete(channel);
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify({
action: 'unsubscribe',
channel: channel
}));
}
}
handleData(data) {
console.log('收到数据:', data);
}
}
const subscriber = new DataSubscriber('wss://api.example.com/ws');
subscriber.connect();
subscriber.subscribe('stock:AAPL');
subscriber.subscribe('stock:GOOGL');
下一步 #
现在你已经掌握了 WebSocket 的基本使用,接下来学习 WebSocket 协议详解,深入了解底层通信机制!
最后更新:2026-03-29