【NodeJs】egg-socket.io从初识到热恋

                                           egg-socket.io

目录

参考资料

Install(下载安装)

Requirements (环境要求)

 Configuration(配置)

egg-socket.io的项目目录结构

开始实践 

配置路由

封装 socket 交互的基本数据格式 

创建auth.js 

创建控制器 

通过调用http接口给客户端发送消息

 更新中...


参考资料

https://eggjs.org/zh-cn/tutorials/socketio.html#namespaceroom

 

Install(下载安装)

$ npm i egg-socket.io --save

Requirements (环境要求)

  • Node.js >= 8.0
  • Egg.js >= 2.0

 Configuration(配置)

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

{app_root}/config/config.default.js

module.exports = appInfo => {
  const config = exports = {
    io: {
      # namespace命名空间配置为/
      namespace: {
        '/': {
          # 预处理器中间件, 我们这里配置了一个auth, 进行权限判断, 它对应的文件是/app/io/middleware/auth.js, 这里可以配置多个文件, 用逗号隔开
          connectionMiddleware: ['auth'], #这里我们可以做一些权限校验之类的操作
          packetMiddleware: [], # 通常用于对消息做预处理,又或者是对加密消息的解密等操作
        },
      },
      # 配置redis, 非必须, 不需要的可以不配置这块, egg-socket.io内置了socket-io-redis, 在cluster模式下, 使用redis可以较为简单的实现clients/rooms等信息共享
      redis: {
        host: 'ip地址',
        prot: 6379,
        auth_pass: 123456,
        db:0, 
      }
    }
  };
  省略 .....
};

到这里egg-socket.io已经开启并配置完毕了 

egg-socket.io的项目目录结构

your-project-name
├── app
│   ├── extend
│   │   └── helper.js
│   ├── io
│   │   ├── controller
│   │   │   └── chat.js
│   │   └── middleware #插件中间件, 基于 socket 模型设计,处理 socket.io 请求 
│   │       ├── auth.js #对应刚才配置的connectionMiddleware: ['auth']
│   └── router.js
├── config
└── package.json

注意:

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

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

开始实践 

配置路由

module.exports = app => {
  const { router, controller, io } = app;
  //http 接口, 在对应的控制器中可以直接操作socket, 非常方便
  router.get('/', controller.home.index);
  router.get('/user', controller.user.index);
  
  // socket, 指向app/io/controller/chat.js的index方法
  io.route('chat', app.io.controller.chat.index);
};

Tip:

  // controller 中
// 发送给自己
this.socket.emit('eventName', 'value');
// 发送给除了自己外的所有人
this.socket.broadcast.emit('eventName', 'value');
// 发送给所有人,包括自己
this.server.sockets.emit('eventName', 'value');

封装 socket 交互的基本数据格式 

// app/extend/helper.ts
// 添加扩展
export default {
    /**
   * 封装 socket 请求数据格式
   * @param action 事件
   * @param payload 数据
   * @param metadata 元信息
   */
    parseMsg(action, payload = {}, metadata = {}) {
        // 封装 meta 数据,添加当前时间轴
        const meta = Object.assign({}, {
            timestamp: Date.now(),
        }, metadata);
        // 格式化返回数据
        return {
            meta,
            data: {
                action,
                payload,
            },
        };
    },
};

创建auth.js 

const room = "youdo_room";
module.exports = () => {
    return async(ctx, next) => {
        const {socket, session} = ctx;
        const query = socket.handshake.query;
        //用户信息
        const { room, userId } = query;
        // 获取客户端ID
        const clientId = socket.id;
        //这里假设用户已登录,ctx.session中已保存了用户登录时颁布的token
        // 校验token
        if (session[query.uid].token !== query.token) { 
             // 踢出用户之前先发送消息
            socket.emit('res', '登录状态已改变,请重新登陆');
            // 调用adapter的remoteDisconnect方法远程控制client断开连接
            socket.adapter.remoteDisconnect(clientId, true, err => {
                console.log(`${ new Date() }\tKick { ${ clientId } } out.`);
            });
            return;
        }
        // 权限校验通过
        ctx.socket.emit('res', 'auth success');
        
        // 检查房间是否存在,不存在则踢出用户
        // 备注:此处 app.redis 与egg-socket.io插件无关,用的是 egg-redis 插件,可用其他存储代替
        const hasRoom = await app.redis.get(`${PREFIX}:${room}`);

        if (!hasRoom) {
          // 没有房间,断开与用户连接
         tick(id, {
            type: 'deleted',
            message: 'deleted, room has been deleted.',
         });
         return;
        }


        // 加入房间 room 存在于 nsp 中,通过 `join/leave` 方法来加入或者离开
        socket.join(room);
      
         // 在线列表
        nsp.adapter.clients(rooms, (_, clients) => {

         // 更新在线用户列表
         // 使用 to 指定房间, emit 往房间发送消息
          nsp.to(room).emit('online', {
             clients,
             action: 'join',
             target: 'participator',
             message: `User(${id}) joined.`,
          });
        });

        // 放行
        await next();
        //断开连接时执行的
        console.log('断开连接');

        // 在线列表
        nsp.adapter.clients(rooms, (_, clients) => {
          // 更新在线用户列表
          // 使用 to 指定房间, emit 往房间发送消息
          nsp.to(room).emit('online', {
            clients,
            action: 'leave',
            target: 'participator',
            message: `User(${id}) leaved.`,
          });
        });
      };

   }
};

创建控制器 

'use strict';

const Controller = require('egg').Controller;
const room = 'default_room';

class ChatController extends Controller {
    async index(){
         const {app, socket, logger, helper} = this.ctx;
         const id = socket.id;
         const nsp = app.io.of('/');
        const message = ctx.args[0] || {};
         // 根据id给指定连接发送消息
         nsp.sockets[id].emit('res', "hello ....");
         // 指定房间连接信息列表
         nsp.adapter.clients([room], (err, clients) => {
             console.log(JSON.stringify(clients));
         });
         //  给指定房间的每个人发送消息
         this.ctx.app.io.of('/').to(room).emit('online', this.ctx.socket.id+ "上线了");
         try {
              const { target, payload } = message;
              if (!target) return;
              // 格式化数据
                  const msg = ctx.helper.parseMsg('exchange', payload, { client, target             });
                  nsp.emit(target, msg);
         } catch (error) {
              app.logger.error(error);
         }

         // 断开连接
         this.ctx.socket.disconnect();
    }
}
module.exports = ChatController;

通过调用http接口给客户端发送消息

const Controller = require('egg').Controller;

class HomeController extends Controller {
  async index() {
    const {app, query} = this.ctx;
    // 给谁发, socket连接的id
    const id = query.id;
    const nsp = app.io.of('/');
    if(nsp.sockets[id]){
       
      let msg = '{"id":2, "message":666}';
      let data = await JSON.parse(msg);
      // 通过id给指定socket连接发送消息
      nsp.sockets[id].emit('res', data);
    }
    this.ctx.body = "发送成功";
  }
}
module.exports = HomeController;

 

 更新中...

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值