基于 ThinkJS 的 WebSocket 通信详解

本文介绍了如何在 ThinkJS 项目中使用 WebSocket 实现多端实时通信,通过 WebSocket 协议、Socket.io 和 ThinkJS 的集成,创建了一个简易聊天室,并探讨了多节点通信解决方案,包括消息队列、Redis 通信和 socket.io-redis 库的使用。
摘要由CSDN通过智能技术生成

编者按:本文转载自 ThinkJS 知乎专栏。

前言

我们的项目是基于 ThinkJS + Vue 开发的,最近实现了一个多端实时同步数据的功能,所以想写一篇文章来介绍下如何在 ThinkJS 的项目中利用 WebSocket 实现多端的实时通信。ThinkJS 是基于 Koa 2 开发的企业级 Node.js 服务端框架,文章中会从零开始实现一个简单的聊天室,希望读者们能有所收获。

WebSocket

WebSocket 是 HTML5 中提出的一种协议。它的出现是为了解决客户端和服务端的实时通信问题。在 WebSocket 出现之前,如果想实现实时消息传递一般有两种方式:

  1. 客户端通过轮询不停的向服务端发送请求,如果有新消息客户端进行更新。这种方式的缺点很明显,客户端需要不停向服务器发送请求,然而 HTTP 请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多带宽资源

  2. HTTP 长连接,客户端通过 HTTP 请求连接到服务端后, 底层的 TCP 连接不会马上断开,后续的信息还是可以通过同一个连接来传输。这种方式有一个问题是每个连接会占用服务端资源,在收到消息后连接断开,就需要重新发送请求。如此循环往复。

可以看到,这两种实现方式的本质还是客户端向服务端“Pull”的过程,并没有一个服务端主动“Push”到客户端的方式,所有的方式都是依赖客户端先发起请求。为了满足两方的实时通信, WebSocket 应运而生。

WebSocket 协议

首先,WebSocket 是基于 HTTP 协议的,或者说借用了 HTTP 协议来完成连接的握手部分。其次,WebSocket 是一个持久化协议,相对于 HTTP 这种非持久的协议来说,一个 HTTP 请求在收到服务端回复后会直接断开连接,下次获取消息需要重新发送 HTTP 请求,而 WebSocket 在连接成功后可以保持连接状态。下图应该能体现两者的关系:

640?wx_fmt=png

在发起 WebSocket 请求时需要先通过 HTTP 请求告诉服务端需要将协议升级为 WebSocket。

浏览器先发送请求:

服务端回应请求:

 
  

HTTP/1.1 101 Switching Protocols

Connection:Upgrade

Upgrade: WebSocket

Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=

在请求头中核心的部分是 Connection 和 Upgrade ,通过这两个字段服务端会将 HTTP 升级为 WebSocket 协议。服务端返回对应信息后连接成功,客户端和服务端就可以正常通信了。

随着新标准的推进,WebSocket 已经比较成熟了,并且各个主流浏览器对 WebSocket 的支持情况比较好(不兼容低版本 IE,IE 10 以下)

640?wx_fmt=png


Socket.io

Socket.io (https://socket.io/) 是一个完全由 JavaScript 实现、基于 Node.js、支持 WebSocket 协议的用于实时通信、跨平台的开源框架。它包括了客户端的 JavaScript 和服务器端的 Node.js,并且有着很好的兼容性,会根据浏览器的支持情况选择不同的方式进行通讯,如上面介绍的轮询和 HTTP 长连接。

简易聊天室

对于 WebSocket 目前 ThinkJS 支持了 Socket.io 并对其进行了一些简单的包装,只需要进行一些简单的配置就可
以使用 WebSocket 了。

服务端配置

stickyCluster

ThinkJS 默认采用了多进程模型,每次请求会根据策略输送到不同的进程中执行,关于其多进程模型可以参考《细谈 ThinkJS 多进程模型》(https://zhuanlan.zhihu.com/p/37921649)。 而 WebSocket 连接前需要使用 HTTP 请求来完成握手升级,多个请求需要保证命中相同的进程,才能保证握手成功。这个时候就需要开启 StickyCluster 功能,使客户端所有的请求命中同一进程。修改配置文件 src/config/config.js 即可。

module.exports = {

    stickyCluster: true,

    // ...

}

添加 WebSocket 配置

src/config/extend.js 引入 WebSocket:

 
  

const websocket = require('think-websocket');

module.exports = [

  // ...

  websocket(think.app),

];

src/config/adapter.js 文件中配置 WebSocket

const socketio = require('think-websocket-socket.io');

exports.websocket = {

  type: 'socketio',

  common: {

    // common config

  },

  socketio: {

    handle: socketio,

    messages: {

      open: '/websocket/open', //建立连接时处理对应到 websocket Controller 下的 open Action

      close: '/websocket/close', // 关闭连接时处理的 Action

      room: '/websocket/room' // room 事件处理的 Action

    }

  }

}

配置中的 messages 对应着事件的映射关系。比如上述的例子,客户端触发 room 事件,服务端需要在 websocket controller 下的 roomAction 中处理消息。

添加 WebSocket 实现

创建处理消息的 controller 文件。上面的配置是 /websocket/xxx ,所以直接在项目根目录 src/controller 下创建 websocket.js 文件。

 
  

module.exports = class extends think.Controller {

// this.socket 为发送消息的客户端对应的 socket 实例, this.io 为Socket.io 的一个实例

  constructor(...arg) {

    super(...arg);

    this.io = this.ctx.req.io;

    this.socket = this.ctx.req.websocket;

  }

  async openAction

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值