Skip to content
关注公众号,获取新课通知

socket.io安装和通讯


安装

shell
npm i egg-socket.io --save

文档地址:https://eggjs.org/zh-cn/tutorials/socketio.html

配置:

js
// config/plugin.js
io: {
	enable: true,
	package: 'egg-socket.io',
},

// config/config.default.js
config.io = {
    init: {
      wsEngine: 'ws',
    },
    namespace: {
      '/': {
        connectionMiddleware: [
          'auth',
        ],
        packetMiddleware: [],
      }
    },
    redis: {
      host: '127.0.0.1',
      port: 6379,
	  db: 0,
    },
  };

部署: package.json

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

Nginx 配置

location / {
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $host;
  proxy_pass   http://127.0.0.1:7001;

  # http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_bind
  # proxy_bind       $remote_addr transparent;
}

socket.io插件控制器:app/io/controller/nsp.js

js
"use strict";

const Controller = require("egg").Controller;
const PREFIX = "room";
class NspController extends Controller {
  async checkToken(token) {
    const { ctx, app, service, helper } = this;
    const nsp = app.io.of("/");
    const socket = ctx.socket;
    const id = socket.id;

    // 用户验证
    if (!token) {
      socket.emit(id, ctx.helper.parseMsg("error", "您没有权限访问该接口!"));
      return false;
    }
    //2. 根据token解密,换取用户信息
    let user = {};
    try {
      user = ctx.checkToken(token);
    } catch (error) {
      let fail = error.name === "TokenExpiredError"
        ? "token 已过期! 请重新获取令牌"
        : "Token 令牌不合法!";
      socket.emit(id, ctx.helper.parseMsg("error", fail));
      return false;
    }
    //3. 判断当前用户是否登录
    let t = await ctx.service.cache.get("user_" + user.id);
    if (!t || t !== token) {
      socket.emit(id, ctx.helper.parseMsg("error", "Token 令牌不合法!"));
      return false;
    }

    //4. 获取当前用户,验证当前用户是否被禁用
    user = await app.model.User.findByPk(user.id);
    if (!user) {
      socket.emit(id, ctx.helper.parseMsg("error", "用户不存在或已被禁用"));
      return false;
    }

    return user;
  }
  // 直播间发送消息
  async comment() {
    const { ctx, app, service, helper } = this;
    const nsp = app.io.of("/");
    const message = ctx.args[0] || {};
    const socket = ctx.socket;
    const id = socket.id;

    const { live_id, data, token } = message;

    let user = await this.checkToken(token);
    if (!user) {
      return;
    }

    try {
      // 验证当前直播间是否存在且是否处于开播中
      let msg = await service.live.checkStatus(live_id);
      if (msg) {
        socket.emit(id, ctx.helper.parseMsg("error", msg));
        return;
      }

      const room = "live_" + live_id;
      // 推送消息到直播间
      nsp.to(room).emit("comment", {
        user: {
          id: user.id,
          name: user.nickname || user.username,
          avatar: user.avatar,
        },
        content: data,
      });

      app.model.Comment.create({
        content: data,
        live_id,
        user_id: user.id,
      });
    } catch (error) {
      app.logger.error(error);
    }
  }

  // 进入直播间
  async joinLive() {
    const { ctx, app, service, helper } = this;
    const nsp = app.io.of("/");
    const message = ctx.args[0] || {};
    const socket = ctx.socket;
    const id = socket.id;

    const { live_id, token } = message;

    let user = await this.checkToken(token);
    if (!user) {
      return;
    }

    // 验证当前直播间是否存在且是否处于开播中
    let msg = await service.live.checkStatus(live_id);
    if (msg) {
      socket.emit(id, ctx.helper.parseMsg("error", msg));
      return;
    }

    const room = "live_" + live_id;
    // 用户加入
    console.log("#join", room);
    socket.join(room);

    const rooms = [room];
    // 在线列表
    // nsp.adapter.clients(rooms, (err, clients) => {
    //     console.log('#online_join', clients);
    // });

    // 加入存储中
    let list = await service.cache.get("userList_" + room);
    list = list ? list : [];
    list = list.filter((item) => item.id !== user.id);
    list.unshift({
      id: user.id,
      name: user.nickname || user.username,
      avatar: user.avatar,
    });
    service.cache.set("userList_" + room, list);

    // 更新在线用户列表
    nsp.adapter.clients(rooms, (err, clients) => {
      console.log("#online_join", clients);
      nsp.to(room).emit("online", {
        clients,
        action: "join",
        user: {
          id: user.id,
          name: user.nickname || user.username,
          avatar: user.avatar,
        },
        data: list,
      });
    });

    // 加入播放历史记录
    let liveUser = await app.model.LiveUser.findOne({
      where: {
        user_id: user.id,
        live_id,
      },
    });
    if (!liveUser) {
      app.model.LiveUser.create({
        user_id: user.id,
        live_id,
      });
      // 总观看人数+1
      let live = await service.live.exist(live_id);
      live.increment({
        look_count: 1,
      });
    }
  }

  // 离开直播间
  async leaveLive() {
    const { ctx, app, service, helper } = this;
    const nsp = app.io.of("/");
    const message = ctx.args[0] || {};
    const socket = ctx.socket;
    const id = socket.id;

    const { live_id, token } = message;

    let user = await this.checkToken(token);
    if (!user) {
      return;
    }

    // 验证当前直播间是否存在且是否处于开播中
    let msg = await service.live.checkStatus(live_id);
    if (msg) {
      socket.emit(id, ctx.helper.parseMsg("error", msg));
      return;
    }

    const room = "live_" + live_id;
    // 用户离开
    console.log("#leave", room);
    socket.leave(room);

    const rooms = [room];
    // 在线列表
    nsp.adapter.clients(rooms, (err, clients) => {
      console.log("#online_join", clients);

      // 更新在线用户列表
      nsp.to(room).emit("online", {
        clients,
        action: "leave",
        user: {
          id: user.id,
          name: user.nickname || user.username,
          avatar: user.avatar,
        },
      });
    });

    let list = await service.cache.get("userList_" + room);
    if (list) {
      list = list.filter((item) => item.id !== user.id);
      service.cache.set("userList_" + room, list);
    }
  }

  // 直播间送礼物
  async gift() {
    const { ctx, app, service, helper } = this;
    const nsp = app.io.of("/");
    const message = ctx.args[0] || {};
    const socket = ctx.socket;
    const id = socket.id;

    const { live_id, gift_id, token } = message;

    let user = await this.checkToken(token);
    if (!user) {
      return;
    }

    try {
      // 验证当前直播间是否存在且是否处于开播中
      let live = await service.live.checkStatus(live_id, true);
      if (typeof live === "string") {
        socket.emit(id, ctx.helper.parseMsg("error", live));
        return;
      }

      // 验证礼物是否存在
      let gift = await app.model.Gift.findOne({
        where: { id: gift_id },
      });
      if (!gift) {
        socket.emit(id, ctx.helper.parseMsg("error", "该礼物不存在"));
        return;
      }
      // 金币不足
      if (user.coin < gift.coin) {
        socket.emit(id, ctx.helper.parseMsg("error", "金币不足,请先充值"));
        return;
      }
      // 扣除金币
      user.coin -= gift.coin;
      await user.save();

      // 写入礼物记录表
      app.model.LiveGift.create({
        live_id,
        user_id: user.id,
        gift_id,
      });

      // 直播间增加金币数
      live.coin += gift.coin;
      live.save();

      const room = "live_" + live_id;
      // 推送消息到直播间
      nsp.to(room).emit("gift", {
        avatar: user.avatar,
        username: user.nickname || user.username,
        gift_image: gift.image,
        gift_name: gift.name,
        num: 1,
      });
    } catch (error) {
      app.logger.error(error);
    }
  }
}

module.exports = NspController;

扩展:app/extend/helper.js

js
parseMsg(action, payload = {}, metadata = {}) {
        const meta = Object.assign({}, {
            timestamp: Date.now(),
        }, metadata);

        return {
            meta,
            data: {
                action,
                payload,
            },
        };
    },

插件路由:app/router.js

js
io.of("/").route("comment", io.controller.nsp.comment);
io.of("/").route("joinLive", io.controller.nsp.joinLive);
io.of("/").route("leaveLive", io.controller.nsp.leaveLive);
io.of("/").route("gift", io.controller.nsp.gift);