5.0.3 • Published 20 days ago

egg-socket.io-new v5.0.3

Weekly downloads
-
License
MIT
Repository
github
Last release
20 days ago

egg-socket.io-new

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

egg 框架的 socket.io@4.x 插件

安装

$ npm i egg-socket.io-new --save

环境要求

  • Node.js >= 14.0
  • Egg.js >= 3.0

配置

通过 config/plugin.js 配置启动 Socket.IO 插件:

// {app_root}/config/plugin.js
exports.io = {
  enable: true,
  package: 'egg-socket.io-new',
};

config/config.${env}.js 配置 Socket.IO :

exports.io = {
  init: { }, // passed to engine.io
  namespace: {
    '/': {
      connectionMiddleware: [],
      packetMiddleware: [],
    },
  },
  redis: {
    host: '127.0.0.1',
    port: 6379
  }
};

ws

exports.io = {
  // 下面必去掉,不然socket.io 版本4会报错`this.opts.wsEngine is not a constructor`
  // https://github.com/socketio/socket.io/issues/3859
  // init: { wsEngine: 'ws' },
};

uws

Egg Socket 内部默认使用 ws 引擎,uws 因为某些原因被废止。

如坚持需要使用,请按照以下配置即可:

exports.io = {
  init: { wsEngine: 'uws' },
};
  • 有关更多 init 选项配置,请参考:engine.io
  • 有关更多 Egg Socket 相关默认配置,请参考:config.default.js

generateId

注意: 此函数作为接口预留,便于你按照自己的规则为每一个 socket 生成唯一的 ID:

exports.io = {
  generateId: (request) => {
        // Something like UUID.
        return 'This should be a random unique ID';
    }
};

你的主程序的配置文件(config.default.js)中引用这个中间件:

exports.io = {
    namespace: {
        '/': {
            connectionMiddleware: ['generateId'],
        },
    }
};

部署

Node 配置

由于 Socket.IO 的设计缘故,多进程的 Socket.IO 服务必须在 sticky 模式下才能工作,否则会抛出握手异常。

所以,必须开启 sticky 模式:

$ # 修改 package.json 对应的 npm scripts
$ egg-bin dev --sticky
$ egg-scripts start --sticky

这两个脚本都会使框架启动 cluster 时使用 sticky 模式

startCluster({
  sticky: true,
  ...
});

Nginx 配置

如果你使用了 nginx 做代理转发:

location / {
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_pass   http://127.0.0.1:{ your node server port };
}

用法

目录结构

app
├── io
│   ├── controller
│   │   └── chat.js
│   └── middleware
│       ├── auth.js
│       ├── filter.js
├── router.js
config
 ├── config.default.js
 └── plugin.js

中间件

middleware are functions which every connection or packet will be processed by.

连接中间件

  • 编写连接中间件 app/io/middleware/auth.js
module.exports = app => {
    return async (ctx, next) => {
        ctx.socket.emit('res', 'connected!');
        await next();
        // execute when disconnect.
        console.log('disconnection!');
    };
};
  • 配置使之生效

config/config.default.js

exports.io = {
  namespace: {
    '/': {
      connectionMiddleware: ['auth'],
    },
  },
};

注意,必须配置在特定的命名空间下,才会生效

包中间件

  • 编写包中间件 app/io/middleware/filter.js
module.exports = app => {
    return async (ctx, next) => {
        ctx.socket.emit('res', 'packet received!');
        console.log('packet:', this.packet);
        await next();
    };
};
  • 配置使之生效

config/config.default.js

exports.io = {
  namespace: {
    '/': {
      packetMiddleware: ['filter'],
    },
  },
};

注意,必须配置在特定的命名空间下,才会生效

控制器

controller is designed to handle the emit event from the client.

example:

app/io/controller/chat.js

module.exports = app => {
  class Controller extends app.Controller {
    async ping() {
      const message = this.ctx.args[0];
      await this.ctx.socket.emit('res', `Hi! I've got your message: ${message}`);
    }
  }
  return Controller
};

// or async functions
exports.ping = async function() {
  const message = this.args[0];
  await this.socket.emit('res', `Hi! I've got your message: ${message}`);
};

下一步,在 app/router.js 配置路由

module.exports = app => {
  // or app.io.of('/')
  app.io.route('chat', app.io.controller.chat.ping);
};

路由

路由主要负责对特定的 socket 连接不同的事件进行分发处理到对应的控制器。路由配置写在 app/router.js 中,实例参照上一节。

除了,业务逻辑的路由之外,还有配置几个系统内置事件:

  • disconnecting 正在断开连接
  • disconnect 连接已经断开
  • error 发生了错误

示例:

app/router.js

app.io.route('disconnect', app.io.controller.chat.disconnect);

app/io/controller/chat.js

module.exports = (app) => {
  class Controller extends app.Controller {
    async disconnect() {
      const message = this.ctx.args[0];
      console.log(message);
    }
  }
  return Controller
};

会话

egg-socket.io 支持会话,与普通的 HTTP 会话几乎一样。

会话的创建和鉴权只发生在建立连接阶段。在包中间件和控制器中可以获取通过 ctx.session 获取会话对象,但是它只在连接创建。

会话功能由 egg-session 实现,请确保它已经开启。

集群

如果你的 Socket.IO 服务由多台服务器提供,那么必须思考集群方案。比如,广播,房间等功能,必须依赖集群方案。

egg-socket.io 集成了 socket.io-redis ,能够非常方便的实现集群共享资源与事件分发。

配置起来也很简单:

只需要在 config/config.${env}.js 配置 :

exports.io = {
  redis: {
    host: { redis server host },
    port: { redis server prot },
    auth_pass: { redis server password },
    db: 0,
  }
};

egg 服务在启动时,会尝试连接 redis 服务,成功后,应用会顺利启动。

应用于微信小程序服务端

微信小程序所采用的 WebSocket 协议版本为 13 (可抓包查看),而 socket.io 支持的协议版本为 4 详见 socket.io-protocol

解决方案是可以封装一个适配小程序端的 socket.io.js,Github 上也有现成的库可以参考 wxapp-socket-io 示例代码如下:

// 小程序端示例代码
import io from 'vendor/wxapp-socket-io.js';
	
const socket = io('ws://127.0.0.1:7001');
socket.on('connect', function () {
  socket.emit('chat', 'hello world!');
});
socket.on('res', msg => {
  console.log('res from server: %s!', msg);
});

问题 & 建议

请访问 here.

MIT