egg-socket 配置和使用
nodejs
Socket.IO
Socket.IO 是一个基于 Node.js 的实时应用程序框架,在即时通讯、通知与消息推送,实时分析等场景中有较为广泛的应用。
WebSocket 的产生源于 Web 开发中日益增长的实时通信需求,对比基于 http 的轮询方式,它大大节省了网络带宽,同时也降低了服务器的性能消耗;socket.io 支持 websocket、polling 两种数据传输方式以兼容浏览器不支持 WebSocket 场景下的通信需求。
框架提供了 egg-socket.io插件,增加了以下开发规约:
- namespace: 通过配置的方式定义 namespace(命名空间)
- middleware: 对每一次 socket 连接的建立/断开、每一次消息/数据传递进行预处理
- controller: 响应 socket.io 的 event 事件
- router: 统一了 socket.io 的 event 与 框架路由的处理配置方式
安装
$ npm i egg-socket.io --save
开启插件
// {app_root}/config/plugin.js exports.io = { enable: true, package: 'egg-socket.io', };
配置
// {app_root}/config/config.${env}.js exports.io = { init: { }, // passed to engine.io namespace: { '/': { connectionMiddleware: [], packetMiddleware: [], // 针对消息的处理暂时不实现 }, }, };
开启 egg-socket.io 的项目目录结构
chat ├── app │ ├── extend │ │ └── helper.js │ ├── io │ │ ├── controller │ │ │ └── default.js │ │ └── middleware │ │ ├── connection.js │ │ └── packet.js │ └── router.js ├── config └── package.json
注意:对应的文件都在 app/io 目录下
Router 配置 (路由负责将 socket 连接的不同 events 分发到对应的 controller),不用controller方法可以暂时不用配置
// {app_root}/app/router.js module.exports = app => { const { router, controller, io } = app; // default router.get('/', controller.home.index); // socket.io io.of('/').route('server', io.controller.home.server); };
egg-socket.io中间件负责 socket 连接的处理
// {app_root}/app/io/middleware/auth.js const PREFIX = 'room'; //定义房间号 module.exports = () => { return async (ctx, next) => { const { app, socket, logger, helper } = ctx; const id = socket.id; const nsp = app.io.of('/'); const query = socket.handshake.query; //获取socket链接传过来的参数 // 用户信息 const { room, userId } = query; const rooms = [ room ]; logger.debug('#user_info', id, room, userId); const tick = (id, msg) => { logger.debug('#tick', id, msg); // 踢出用户前发送消息 socket.emit(id, helper.parseMsg('deny', msg)); // 调用 adapter 方法踢出用户,客户端触发 disconnect 事件 nsp.adapter.remoteDisconnect(id, true, err => { logger.error(err); }); }; // 检查房间是否存在,不存在则踢出用户 // 备注:此处 app.redis 与插件无关,可用其他存储代替 const hasRoom = await app.redis.get(`${PREFIX}:${room}`); logger.debug('#has_exist', hasRoom); if (!hasRoom) { tick(id, { type: 'deleted', message: 'deleted, room has been deleted.', }); return; } // 用户加入 logger.debug('#join', room); socket.join(room); // 在线列表 nsp.adapter.clients(rooms, (err, clients) => { logger.debug('#online_join', clients); // 更新在线用户列表 nsp.to(room).emit('online', { clients, action: 'join', target: 'participator', message: `User(${id}) joined.`, }); }); await next(); // 用户离开 logger.debug('#leave', room); // 在线列表 nsp.adapter.clients(rooms, (err, clients) => { logger.debug('#online_leave', clients); // 获取 client 信息 // const clientsDetail = {}; // clients.forEach(client => { // const _client = app.io.sockets.sockets[client]; // const _query = _client.handshake.query; // clientsDetail[client] = _query; // }); // 更新在线用户列表 nsp.to(room).emit('online', { clients, action: 'leave', target: 'participator', message: `User(${id}) leaved.`, }); }); }; };
Controller 对客户端发送的 event 进行处理;
// {app_root}/app/io/controller /nsp.js 'use strict'; const Controller = require('egg').Controller; class NspController extends Controller { } module.exports = NspController;
使用方法
async index(){ const nsp = this.app.io.of('/'); nsp.to('app1').emit("step",{数据}); }
vue
安装 sockrt.io 和 调用
$ npm i socket.io-client –save
import io from 'socket.io-client';
vue.config.js配置
'/socket.io': { target: 'http://127.0.0.1:7001', changeOrigin: true, ws:true, pathRewrite: { '^/socket.io': '/socket.io' } }
使用方法
//createIdSocket方法在mounted里面调用就可以
createIdSocket(){ const socket = io.connect(location.origin, { transports: ['websocket'], //传输方式为'websocket' query: { sessionId: 'app1' //app1为房间号 } }); socket.on("connect", () => { }); socket.on("step", msg => { console.log('msg',msg); //返回数据的处理 }); //重新连接时,将transports选项重置为Websocket socket.on('reconnect_attempt', () => { socket.io.opts.transports = ['polling', 'websocket']; }); }
注:socket 数据通信通过房间号建立联系确定获取数据内容