Skip to content

WebSocket

简介

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得简单,允许服务端主动向客户端推送数据。浏览器与服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

很多网站为了实现推送技术,使用的都是 Ajax 轮询,由浏览器对服务器发出 HTTP 请求,然后由服务器返回最新的数据给客户端。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而 HTTP 请求可能包含较长的头部,其中真正有效的数据只占很小的一部分,显然这样做浪费了很多的带宽资源。

HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

websocket

WebSocket 特点:

  • 建立在 TCP 协议之上,服务器端的实现比较容易

  • 与 HTTP 协议有着良好的兼容性,默认端口也是 80 和 443

  • 数据格式比较轻量,性能开销小,通信高效

  • 可以发送文本,也可以发送二进制数据

  • 没有同源限制,客户端可以与任意服务器通信

  • 协议标识符是 ws(如果加密,则为 wss),服务器网址就是 URL

API

Websocket()

Websocket() 是一个构造函数,用于创建新的 Websocket 实例。

执行完下面的语句以后,客户端和服务端便进行了连接:

js
const socket = new Websocket("ws://echo.websocket.org");

webSocket.readyState

状态描述
CONNECTING0正在连接
OPEN1连接成功
CLOSING2连接正在关闭
CLOSED3连接已经关闭
js
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;
  default:
    break;
}

webSocket 事件

事件事件处理程序描述
openSocket.onopen连接建立时触发
messageSocket.onmessage客户端接收服务端数据时触发
errorSocket.onerror通信发生错误时触发
closeSocket.onclose连接关闭时触发

实例对象的 onopen 属性,用于指定连接成功后的回调函数:

js
Socket.onopen = function () {
  console.log("连接建立!");
};

如果要指定多个回调函数,可以使用 addEventListener 方法:

js
Socket.addEventListener("open", function (e) {
  console.log("连接建立!");
});

webSocket 方法

方法描述
Socket.send()使用连接发送数据
Socket.close()关闭连接

下面是一个发送文本的例子:

js
Socket.send("ping");

发送 Blob 对象的例子:

js
var file = document.querySelector("input[type="file"]").files[0];

Socket.send(file);

原生封装 WebSocket

js
export default class WebsocketManager {
  constructor(url) {
    this.url = url
    this.socket = null // websocket实例
    this.pingTimeout = null // 心跳计时器
    this.reconnectTimeout = 5000 // 重连间隔,单位:毫秒
    this.maxReconnectionAttempts = 10 // 最大重连次数
    this.reconnectAttempts = 0 // 当前重连尝试次数
  }

  start() {
    if (this.url) {
      this.connectWebsocket()
    } else {
      console.error("WebSocketManager erros: 请传入连接地址和用户id")
    }
  }

  connectWebsocket() {
    this.socket = new WebSocket(this.url)

    // 处理连接打开事件
    this.socket.addEventListener("open", () => {
      this.startHeartbeat()
    })

    // 处理接收到消息事件
    this.socket.addEventListener("message", event => {
      const data = JSON.parse(event.data)
      this.receiveMessage(data)
    })

    // 处理连接关闭事件
    this.socket.addEventListener("close", () => {
      clearTimeout(this.pingTimeout)
      clearTimeout(this.reconnectTimeout)

      if (this.reconnectAttempts < this.maxReconnectionAttempts) {
        this.reconnectAttempts++
        this.reconnectTimeout = setTimeout(() => {
          this.connectWebsocket()
        }, this.reconnectTimeout)
      } else {
        // 重置连接次数
        this.reconnectAttempts = 0
        console.error(
          "WebSocketManager erros: Max reconnect attempts reached. Unable to reconnect."
        )
      }
    })

    // 处理错误事件
    this.socket.addEventListener("error", error => {
      console.error("WebSocketManager error:", error)
    })
  }

  startHeartbeat() {
    this.pingTimeout = setTimeout(() => {
      if (this.socket.readyState === WebSocket.OPEN) {
        this.socket.send("ping")
      } else {
        console.error(
          "WebSocketManager error: WebSocket connection is not open. Unable to send message."
        )
      }
    }, 10000)
  }

  closeWebSocket() {
    this.socket.close()
    clearTimeout(this.pingTimeout)
    clearTimeout(this.reconnectTimeout)
    this.reconnectAttempts = 0
  }

  receiveMessage(event) {
    switch (event.type) {
      case 1:
        console.log("用户登录:", JSON.parse(event.content))
        break
      case 5:
        console.log("车辆数据:", JSON.parse(event.content))
        break
      case 11:
        console.log("飞机数据:", JSON.parse(event.content))
        break
      default:
        break
    }
  }
}
js
const websocketManager = new WebsocketManager(
  `ws://103.254.68.211:22244/wsquery?loginType=1&token=${token}`
)
websocketManager.start()

Released under the MIT License.