概述
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;
更新中...
最后
以上就是寂寞铃铛为你收集整理的【NodeJs】egg-socket.io从初识到热恋 egg-socket.io的全部内容,希望文章能够帮你解决【NodeJs】egg-socket.io从初识到热恋 egg-socket.io所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复