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 数据通信通过房间号建立联系确定获取数据内容

原文地址:https://www.cnblogs.com/Li--gm/p/13468110.html