用node實現一個簡單的聊天室——websocket實踐

xanggang發表於2018-08-13

websocket是HTML5開始提供的一種在單個TCP連線上進行全雙工通訊的協議。大多數 Web 應用程式將通過頻繁的非同步JavaScript和XML(AJAX)請求實現長輪詢。輪詢的效率低,非常浪費資源。而websocket能夠很好的解決類似的問題。常用於即時通訊、監控等情況。 在本案例中, 使用egg+typescript作為後端框架, 使用egg官方封裝的Socket.IO庫。前端使用vue。

egg配置

TS下的egg許多設定和文件有所不同, 但是稍稍看一下還是能理解的。 按照egg的檔案規劃,Socket.IO應該獨立的作為一個資料夾, 內部包含自己的controller和middleware, 然後和其他頁面共享router配置

// router
export default (app: Application) => {
  const { controller, router, io } = app;

    // Socket.IO會通過io暴露出來, io.of('/')則是名稱空間
  router.get('/', controller.home.index); // 這個是正常的http請求
  io.of('/').route('online', io.controller.nsp.exchange); // 這個就是websocket請求
  io.of('/').route('newmsg', io.controller.nsp.newmsg);
  io.of('/').route('sendMsg', io.controller.nsp.sendMsg);
};
複製程式碼
// config/plugin  這裡是egg開啟外掛外掛的地方
const plugin: EggPlugin = {
  // static: true,
  nunjucks: {
    enable: true,
    package: 'egg-view-nunjucks',  // 模板渲染外掛
  },
  io: {
    enable: true,
    package: 'egg-socket.io', // 官方的封裝的socke外掛
  },
};
複製程式碼
// config/config.default 根據不同環境, 配置外掛引數
export default (appInfo: EggAppInfo) => {
  // ...
  config.middleware = [];
  // 配置模板引擎
  config.view = {
    cache: false,
    defaultExtension: 'nunjucks',
    mapping: {
      '.html': 'nunjucks',
    },
  };
  // 配置socket
  config.io = {
    init: { }, // passed to engine.io
    namespace: {
    // 名稱空間
      '/': {
        connectionMiddleware: [
          'connection', // 這個是連線中介軟體, 只在connection的時候觸發
        ],
        packetMiddleware: [], // 這個會在每次訊息的時候觸發
      },
      '/example': {
        connectionMiddleware: [],
        packetMiddleware: [],
      },
    },
  };

  return config;
};

複製程式碼
// app/router
export default (app: Application) => {
  const { controller, router, io } = app;

  router.get('/', controller.home.index);
  
  // 這裡的sendMsg相當於一個介面, 負責處理客戶端傳送的sendMsg事件
  // 這個controller是io模組的controller, 和egg的controller不同
  io.of('/').route('sendMsg', io.controller.nsp.sendMsg);
};
複製程式碼
// app/io/controller

import { Controller } from 'egg';

export default class NspController extends Controller {

  public async sendMsg() {
    const { ctx, app } = this;
    const nsp = app.io.of('/');
    const message = ctx.args[0] || {}
    // 向客戶端廣播訊息, 在客戶端監聽broadcast事件就可以獲取訊息了
    nsp.emit('broadcast', message)
  }
}

複製程式碼
// app/io/middleware/connection
import { Context } from 'egg';
// io模組的中介軟體, 在config/config.default裡配置成connectionMiddleware, 只在
connection的時候觸發
export default function robotMiddleware() {
  return async (ctx: Context, next: any) => {
    const { app } = ctx;
    const nsp = app.io.of('/');
    // 向客戶端推送online事件
    nsp.emit('online', '有新成員加入聊天室了')
    await next();
  };
}

複製程式碼

這樣, 伺服器端的邏輯就完成了,接下來通過vue來實現客戶端邏輯

用node實現一個簡單的聊天室——websocket實踐
簡單的一個頁面, 分為MsgItem和Send 兩個元件, 具體實現就不寫了。

// 客戶端 src/utils/io
import io from 'socket.io-client';
// 稍微封裝一下socket.io, 然後暴露出去。
const socket = function ():any {
  const _io = io('http://127.0.0.1:7001/');
  _io.on('connect', function(){
    console.log('連結成功');
  });
  _io.on('disconnect', function(){
    console.log('斷開連級');
  });
  return _io
}


export default socket

複製程式碼

用node實現一個簡單的聊天室——websocket實踐

// 客戶端 app/App.vue

  export default class Index extends Vue {
    // 頭像
    user = {
      avatar: 'https://f12.baidu.com/it/u=4263977612,1595937908&fm=76'
    }
    // 訊息列表
    msgList: Array<string> = []

    //傳送訊息, 觸發sendMsg事件
    sendMsg(msg: string):void {
      socket.emit('sendMsg', msg)
    }

    // 頁面載入之後觸發
    mounted() {
      // 監聽online事件
      socket.on('online', (data: string) => {
        this.msgList.push(data)
      })
      // 監聽broadcast事件, 獲取伺服器訊息
      socket.on('broadcast', (data: string) => {
        this.msgList.push(data)
      })
    }
  }
複製程式碼

socket是基於事件監聽來進行的, 通過on來註冊監聽事件, 通過emit來觸發事件。 通過伺服器和客戶端的配合, 就可以事件即時的訊息推送。這篇文章只寫了最簡單功能, 基於socket.io還有分房間, 踢人,身份識別,私聊等功能, 等下篇文章再寫吧。

用node實現一個簡單的聊天室——websocket實踐

相關文章