WebSocket详解

发布于:2025-07-27 ⋅ 阅读:(17) ⋅ 点赞:(0)

一.什么是 WebSocket?

WebSocket 是一种在单个 TCP 连接上进行全双工(双向)通信的协议。它是为了弥补传统 HTTP 协议在实时通信场景中的不足而设计的,允许浏览器和服务器之间进行低延迟的实时数据传输

 二.WebSocket的优势

WebSocket 与 HTTP 的区别

特性 WebSocket HTTP
协议类型 双向通信协议 请求-响应协议
连接状态 持久连接 每个请求都需要建立新连接
数据传输方式 双向全双工通信 客户端发起请求,服务器响应
连接开销 低开销(连接一次即可) 每个请求都需要建立新的连接
实时性 实时,适合长时间的数据交换 每次请求/响应有延迟,适合短期通信
适用场景 实时聊天、在线游戏、推送通知 静态网页加载、表单提交等

 

​​​​

我们再来看看与其他实时通讯技术的对比

通信方式 描述 优点 缺点
轮询 客户端以固定时间间隔(如每秒)向服务器发送 HTTP 请求,询问是否有新数据。每次请求都需要建立新连接。 - 简单易实现 - 延迟高:每次请求都需要建立新连接,延迟较大。
- 带宽浪费:频繁发送无效请求。
- 服务器负担大:每个请求都需要重新建立连接并处理。
长轮询  客户端向服务器发送请求,服务器保持连接,直到有新数据时才响应客户端。减少了连接的频繁建立。 - 减少连接建立开销:相较于普通轮询,减少了频繁的连接请求。
- 更低的延迟:由于连接持续存在。
- 仍然存在延迟:客户端必须等待服务器推送新数据。
- 资源消耗大:等待响应之后再关闭,还是有重复连接
 SSE 服务器单向推送数据到客户端,支持实时数据流,但只支持单向通信。 - 实时推送:服务器可以随时推送数据给客户端。
- 实现简单:只需服务器推送数据。
- 单向通信:不支持客户端向服务器发送数据。
- 不适合双向实时应用:无法进行双向数据交互。
- 浏览器兼容性差:部分浏览器不完全支持 SSE。

  三.WebSocket的属性和方法

WebSocket 属性

属性 描述 示例
url 连接的目标 URL。 new WebSocket('wss://example.com');
readyState 返回 WebSocket 连接的当前状态:0(连接中)、1(已连接)、2(关闭中)、3(已关闭)。 console.log(socket.readyState);
bufferedAmount 当前缓冲区中待发送的数据大小(字节)。 console.log(socket.bufferedAmount);
protocol 连接时使用的子协议(如果有)。 console.log(socket.protocol);
extensions 返回连接时使用的扩展协议(如果有)。 console.log(socket.extensions);


WebSocket 方法

方法 描述 示例
send() 向服务器发送消息(文本或二进制)。 socket.send("Hello, Server!");
close() 关闭 WebSocket 连接。 socket.close();


WebSocket 事件

事件 描述 触发时机
onopen 连接成功建立时触发。 连接成功后触发
onmessage 接收到服务器消息时触发。 当服务器发送消息时触发
onclose 连接关闭时触发。 连接被关闭时触发
onerror 发生错误时触发。 连接发生错误时触发


WebSocket 连接状态常量(readyState

状态值 描述
0 (CONNECTING) 连接正在建立中。
1 (OPEN) 连接已成功建立,可以进行数据交换。
2 (CLOSING) 连接正在关闭。
3 (CLOSED) 连接已关闭或无法建立连接。

 

 实例:

function webSocketManager(options) {
  if (!options.url) {
    throw new Error("url必传");
  }
  if (!options.bizMessageHandler) {
    throw new Error("bizMessageHandler必传");
  }
  this.url = options.url;
  this.heartbeatInterval = options.heartbeatInterval || 10; // 心跳间隔,单位:秒
  this.heartbeatStopCount = options.heartbeatStopCount || 3; // 心跳几次没响应就停止,默认3次
  this.autoReconnect = options.autoReconnect || true; // 断开后是否自动重连
  this.reconnectMinDuration = options.reconnectMinDuration || 10; // 重连最小间隔时间,单位:秒
  this.bizMessageHandler = options.bizMessageHandler; // 业务消息处理函数
  this.onConnectionOpen = options.onConnectionOpen; // 建立连接时回调
}

webSocketManager.prototype.init = function() {
  this._connect();
};

webSocketManager.prototype._startHeartbeat = function() {
  let _this = this;
  this._log("start heartbeat");
  this.lastSendSeq = 0; // 最后一次发出的序列号
  this.lastReceiveSeq = 0; // 最后一次收到的序列号

  this.webSocket.addEventListener("message", function(e) {
 
    let data = parseInt(e.data);
    if (!isNaN(data)) {
      _this.lastReceiveSeq = data;
    }
  });

  this.heartbeatTimerId = window.setInterval(function() {

    let noRespCount = _this.lastSendSeq - _this.lastReceiveSeq;
    if (noRespCount >= _this.heartbeatStopCount) {
      _this._log(`心跳包未响应超过${_this.heartbeatStopCount}次, 主动断开连接`);
      _this.webSocket.close();
      _this._stopHeartbeat();
      return;
    }
    _this.webSocket.send(_this.lastSendSeq++);
  }, this.heartbeatInterval * 1000);
};

webSocketManager.prototype._stopHeartbeat = function() {
  if (null != this.heartbeatTimerId) {
    this._log("stop heartbeat");
    window.clearInterval(this.heartbeatTimerId);
    this.heartbeatTimerId = null;
  }
};

// 连接建立
webSocketManager.prototype._connect = function() {
  let _this = this;
  this.webSocket = new WebSocket(this.url);
  this.connectTime = new Date().getTime();

  // 连接建立成功
  this.webSocket.onopen = function() {
    _this._log("onopen");
    if (_this.onConnectionOpen) {
      _this.onConnectionOpen();
    }
    _this._startHeartbeat();
  };

  // 连接建立失败
  this.webSocket.onerror = function() {
    _this._log("onerror");
  };

  // 连接断开
  this.webSocket.onclose = function() {
    _this._log("onclose");
    _this._stopHeartbeat();
    // 如果设置为自动重连就自动重连
    if (_this.autoReconnect) {
      // 为了避免断网时频繁不停重连的情况,需要判断下上次连接时间
      let duration = new Date().getTime() - _this.connectTime;
      // 如果重连小于最小间隔的时间,则延迟再重连,否则马上重连
      if (duration < _this.reconnectMinDuration * 1000) {
        setTimeout(function() {
          _this._connect();
        }, _this.reconnectMinDuration * 1000 - duration);
      } else {
        _this._connect();
      }
    }
  };

  this.webSocket.addEventListener("message", function(e) {
    let msg = e.data;
    // _this._log(`onmessage; message is ${msg}`);
    // 如果是业务消息则回调业务消息处理函数
    if (_this._isBizMessage(msg)) {
      _this.bizMessageHandler(msg);
    }
  });
};

webSocketManager.prototype._isBizMessage = function(msg) {
  return msg && msg.indexOf("{") === 0;
};

webSocketManager.prototype._log = function(msg) {
  if (console && console.log) {
    console.log(`[webSocketManager] ${new Date().getTime()} ${msg}`);
  }
};

export default webSocketManager;


网站公告

今日签到

点亮在社区的每一天
去签到