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