ewp

egg-websocket-plugin

A standard WebSocket plugin for egg.js

Showing:

Popularity

Downloads/wk

98

GitHub Stars

26

Maintenance

Last Commit

7mos ago

Contributors

3

Package

Dependencies

3

License

MIT

Type Definitions

Built-In

Tree-Shakeable

No?

Categories

Readme

egg-websocket-plugin

NPM version build status Test coverage David deps Known Vulnerabilities npm download

依赖说明

依赖的 egg 版本

✅ egg 2.x

特性支持

  • 兼容 egg 自身的 controller
  • 兼容 egg 路由中间件
  • 不需要像 socket.io 一样开启 sticky 模式
  • 支持消息广播订阅

使用方式

yarn add egg-websocket-plugin

1. 开启插件

// config/plugin.js
exports.websocket = {
  enable: true,
  package: 'egg-websocket-plugin',
};

2. 配置 WebSocket 路由

// app/router.js
app.ws.route('/ws', app.controller.home.hello);

// 可使用路由参数,使用方式同 egg 自身路由
app.ws.route('/foo/:id', app.controller.home.foo);

3. 配置 WebSocket 全局中间件【可选】

注意:配置 WebSocket 全局中间件应当在所有 app.ws.route 注册路由之前进行,否则之前的路由不会应用到全局中间件,且插件会生成一条警告日志

// app/router.js

// 配置 WebSocket 全局中间件
app.ws.use(async (ctx, next) => {
  console.log('websocket open');
  await next();
  console.log('websocket closed');
});

4. 配置路由中间件【可选】

路由会依次用到 app.use, app.ws.use, 以及 app.ws.router 中配置的中间件

// app/router.js
async function middleware(ctx, next) {
  console.log('open', ctx.starttime);
  await next();
}
// 配置路由中间件
app.ws.route('/ws', middleware, app.controller.home.hello);

5. 禁用 App 全局中间件

插件默认会使用 app.use(...) 注册的中间件,如果你不想使用它们,或者这些插件与 websocket 有冲突,你可以在 config.default.js 中禁用它们

config.websocket = {
  useAppMiddlewares: false,
};

6. 在控制器中使用 websocket

websocket 是一个 ws 插件的实例,可阅读 ws 插件的说明文档或 TypeScript 的定义

// app/controller/home.js
import { Controller } from 'egg';

export default class HomeController extends Controller {
  async hello() {
    const { ctx, app } = this;
    if (!ctx.websocket) {
      throw new Error('this function can only be use in websocket router');
    }

    console.log('client connected');

    ctx.websocket
      .on('message', (msg) => {
        console.log('receive', msg);
      })
      .on('close', (code, reason) => {
        console.log('websocket closed', code, reason);
      });
  }
}

支持 Redis 消息发布订阅【可选】

开启消息广播

注意egg-websocket-plugin 依赖于 ioredis 但可与其他插件如 egg-redis 共用一个 ioredis 依赖

开启广播相关的 API 需要在 config.default.js 中配置 redis 连接,配置项可参见 ioredis

config.websocket = {
  // 配置 websocket 使用 redis 作消息广播,配置项目参见 ioredis
  redis: {
    host: '127.0.0.1',
    port: 6379,
  },
};

房间消息广播 API

开启 redis 配置后,即可使用下面的广播 API

1. app.ws.sendTo(room, data)

发送消息到某个房间,将 data 原样发送到客户端连接

  • room <string>: 房间名
  • data <string | Buffer>: 发送内容,原样发送

2. app.ws.sendJsonTo(room, data)

发送消息到某个房间,会自动使用 JSON.stringify 对 data 进行序列化

  • room <string> 房间名
  • data <any> 发送内容,可进行 JSON 序列化的任意对象

3. ctx.websocket.room.join(room, fn?)

将当前连接加入房间

  • room <string> 要加入的房间名
  • fn <function({room, message})> 可选参数,用于配置房间消息处理函数,配置详情见 自定义房间消息处理函数
    • room <string> 消息所属房间名
    • message<string | Buffer> 房间内的广播消息,消息类型取决于 sendTo() 发送时的类型
    • 忽略此参数,则该房间的消息将直接发送到客户端连接
    • 配置这个参数,则可以手动处理消息发送逻辑,不再自动将房间内的消息发送到客户端连接

4. ctx.websocket.room.leave(room)

当前连接离开房间,当前连接关闭后,将自动退出当前连接加入过的房间,因此关闭连接前无需要手动离开房间

  • room <string> 房间名

5. ctx.websocket.room.sendTo(room, data)

发送消息到某个房间,将 data 原样发送到客户端连接,功能与 app.ws.sendTo 相同

6. ctx.websocket.room.sendJsonTo(room, data)

发送消息到某个房间,会自动使用 JSON.stringify 对 data 进行序列化,功能与 app.ws.sendTo 相同

// 当前连接加入房间 foo,收到房间的消息将直接发送到当前客户端连接
ctx.websocket.room.join('foo');

// 当前连接加入房间 bar,并设置该房间消息处理函数,设置了处理函数将不再直接发送消息到当前客户端
// room: string, message: Buffer | string
ctx.websocket.room.join('bar', ({ room, message }) => {
  console.log(room, message);
});

// 当前连接离开房间
ctx.websocket.room.leave('foo');

// 通过当前 ctx 广播消息,所有加入房间的连接会收到这条消息
ctx.websocket.room.sendTo('foo', 'hello foo');

// 也可以通过通过 app 对象来广播消息
app.ws.sendTo('bar', 'hello bar');

自定义房间消息处理函数

注意:使用 room.join() 函数,忽略第二个参数的情况下,房间内的消息会直接发送到当前客户端连接。

如果你需要自定义这部分的处理逻辑,那么可配置 room.join() 的第二个参数。

ctx.websocket.room.join('foo', ({ room, message }) => {
  // room: string 房间名
  // message: Buffer | string 消息内容
  // message 的类型取决于使用 sendTo 发送的是 string 还是 Buffer
  console.log(room, message);
});

例子

router.ts

import { Application } from 'egg';

export default (app: Application) => {
  const { controller, router } = app;

  // 配置 http 路由
  router.get('/', controller.home.index);
  // 配置 websocket 路由,websocket 可与 http 使用同一个 url,不会导致冲突
  app.ws.route('/', controller.home.ws);
};

controller/home.ts

import { Controller } from 'egg';

export default class HomeController extends Controller {
  public async index() {
    const { ctx, app } = this;
    // 向房间 foo 发送消息
    app.ws.sendTo('foo', 'hello from index');
    ctx.body = 'index';
  }

  public async ws() {
    const { websocket } = this.ctx;
    if (!websocket) {
      throw new Error('this function can only be use in websocket router');
    }

    // 加入到房间 foo
    websocket.room.join('foo');
  }
}

浏览器连接 WebSocket

const ws = new WebSocket('ws://127.0.0.1:7001/');
ws.addEventListener('message', data => {
  console.log(data);
});

这时候,访问 http://localhost:7001/ WebSocket 客户端将会收到一条 hello from index 的字符串

使用场景

  • 使用 egg 开发标准 WebSocket 服务器
  • 微信小程序 WebSocket 服务器

提问交流

请到 issues 异步交流。

License

MIT

Rate & Review

Great Documentation0
Easy to Use0
Performant0
Highly Customizable0
Bleeding Edge0
Responsive Maintainers0
Poor Documentation0
Hard to Use0
Slow0
Buggy0
Abandoned0
Unwelcoming Community0
100
No reviews found
Be the first to rate

Alternatives

No alternatives found

Tutorials

No tutorials found
Add a tutorial