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 數據通信通過房間號建立聯系確定獲取數據內容