Socket.io

安装:

$ yarn add egg-socket.io

配置Socket.io

config/plugin.js

exports.io = {
  enable: true,
  package: 'egg-socket.io',
};

config/config.${env}.js

exports.io = {
  init: {
    wsEngine: 'ws', // default: ws
  },
  redis: { // redis
    host: '127.0.0.1',
    port: 6379,
    auth_pass: 'root',
    db: 0,
  },
  namespace: { // 命名空间
    '/': {
      connectionMiddleware: [],
      packetMiddleware: [],
    },
  },
};

注意

如果项目中同时使用了 egg-redis,请单独配置,不可共用。

框架是以 Cluster 方式启动的,而 socket.io 协议实现需要 sticky 特性支持,否则在多进程模式下无法正常工作。

由于 socket.io 的设计,在多进程中服务器必须在 sticky 模式下工作,故需要给 startCluster 传递 sticky 参数。

修改 package.json 中 npm scripts 脚本:

{
  "scripts": {
    "dev": "egg-bin dev --sticky",
    "start": "egg-scripts start --sticky"
  }
}

修改路由

app/router.js

'use strict';
/**
 * @param {Egg.Application} app - egg application
 */
module.exports = ({ router, controller, io }) => {
  // 普通路由
  router.get('/', 'home.index');
  // socket.io
  io.of('/').route('msg', io.controller.default.ping);
};

socket.io 对应的文件都在 app/io 目录下

创建控制器

app/io/controller/default.js

'use strict';
exports.msg = async function() {
  const message = this.args[0];
  console.log(message);
  await this.socket.emit('msg', `Hi! I've got your message: ${JSON.stringify(message)}`);
};

或者

'use strict';
const Controller = require('egg').Controller;
class DefaultController extends Controller {
  async msg() {
    const { ctx } = this;
    const message = ctx.args[0];
    console.log(ctx.socket.id, message);
    await ctx.socket.emit('msg', `Hi! I've got your message: ${JSON.stringify(message)}`);
  }
}
module.exports = DefaultController;

这样, 一个简单的服务器端示例完成了, 接下来, 就是客户端连接, 跟平常写 socket.io 客户端没什么两样:

创建客户端

<!doctype html>
<html>
<head>
  <title>Socket.IO chat</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { font: 13px Helvetica, Arial; }
    form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
    form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
    form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
    #messages { list-style-type: none; margin: 0; padding: 0; }
    #messages li { padding: 5px 10px; }
    #messages li:nth-child(odd) { background: #eee; }
  </style>
</head>
<body>
  <ul id="messages"></ul>
  <form action="">
    <input id="m" autocomplete="off" />
    <button>Send</button>
  </form>
  <script src="https://cdn.bootcss.com/socket.io/2.1.0/socket.io.js"></script>
  <script src="https://code.jquery.com/jquery-1.11.1.js"></script>
  <script>
    window.onload = function () {
      var socket = io('http://localhost:7001/');
      window.socket = socket
      socket.on('msg', function(msg){
        $('#messages').append($('<li>').text(msg));
      });
      $('form').submit(function(){
        socket.emit('msg', $('#m').val());
        $('#m').val('');
        return false;
      });
    }
  </script>
</body>
</html>

上面的示例, 客户端通过input框的输入信息发送到服务端, 服务器再将接收到的信息回传到客户端

注意到, 这里使用的是 socket.emit('msg', val);, 对应的为路由定义中的 io.of('/').route('msg', io.controller.default.ping);, msg 就是 socket.io 中定义的事件名

参考资料

更多关于 Socket.io 的知识请参考我的另一本书 socket.io

MIT Licensed | Copyright © 2018-present 滇ICP备16006294号

Design by Quanzaiyu | Power by VuePress