ThinkJS WebSocket(即時通訊)

2021-09-17 10:27 更新

項目里經(jīng)常要要使用 WebSocket 來實現(xiàn)聊天等功能,ThinkJS 支持多種 WebSocket 庫,如:socket.io,sockjs 等,并對這些庫進行了一些簡單的包裝,讓使用的接口一致。

開啟 WebSocket

WebSocket 功能默認是關(guān)閉的,項目如果需要開啟,可以修改配置文件 src/common/config/websocket.js

export default {
  on: false, //是否開啟 WebSocket
  type: "socket.io", //使用的 WebSocket 庫類型,默認為 socket.io
  allow_origin: "", //允許的 origin
  adapter: undefined, // socket 存儲的 adapter,socket.io 下使用
  path: "", //url path for websocket
  messages: {
    // open: "home/websocket/open",
  }
};

需要將配置 on 的值修改為 true,并重啟 Node.js 服務。

事件到 Action 的映射

ThinkJS 里對 WebSocket 的包裝遵循了 socket.io 的機制,服務端和客戶端之間通過事件來交互,這樣服務端需要將事件名映射到對應的 Action,才能響應具體的事件。配置在 messages 字段,具體如下:

export default {
  messages: {
    open: "home/socketio/open", // WebSocket 建立連接時處理的 Action
    close: "home/socketio/close", // WebSocket 關(guān)閉時處理的 Action
    adduser: "home/socketio/adduser", //adduser 事件處理的 Action
  }
}

其中 open 和 close 事件名固定,表示建立連接和斷開連接的事件,其他事件均為自定義,項目里可以根據(jù)需要添加。

Action 處理

通過上面配置事件到 Action 的映射后,就可以在對應的 Action 作相應的處理了。如:

export default class extends think.controller.base {
  /**
   * WebSocket 建立連接時處理
   * @param  {} self []
   * @return {}      []
   */
  openAction(self){
    var socket = self.http.socket;
    this.broadcast("new message", {
      username: socket.username,
      message: self.http.data
    });
  }
}

emit

Action 里可以通過 this.emit 方法給當前 socket 發(fā)送事件,如:

export default class extends think.controller.base {
  /**
   * WebSocket 建立連接時處理
   * @param  {} self []
   * @return {}      []
   */
  openAction(self){
    var socket = self.http.socket;
    this.emit("new message", "connected");
  }
}

broadcast

Action 里可以通過 this.broadcast 方法給所有的 socket 廣播事件,如:

export default class extends think.controller.base {
  chatAction(self){
    var socket = self.http.socket;
    //廣播給除當前 socket 之外的所有 sockets
    this.broadcast("new message", {msg: "message", username: "xxx"});
  }
}

注: broadcast 方法默認是給除去當前 socket 的所有 sockets 發(fā)送事件,如果想包含當前的 socket,可以設(shè)置第三個參數(shù)值為 true。

export default class extends think.controller.base {
  chatAction(self){
    var socket = self.http.socket;
    //廣播給所有的 sockets,包含當前的 socket
    this.broadcast("new message", {msg: "message", username: "xxx"}, true);
  }
}

socket 對象

Action 里可以通過 this.http.socket 拿到當前的 socket 對象。

事件數(shù)據(jù)

Action 里可以通過 this.http.data 拿到發(fā)送過來事件的數(shù)據(jù)。

socket.io

socket.io 對 WebSocket 前后端都有封裝,使用起來非常方便。

io 對象

在 Action 里可以通過 this.http.io 來獲取 io 對象,該對象為 socket.io 的一個實例。

io 對象包含的方法請見 [http://socket.io/docs/server-api/#server()](http://socket.io/docs/server-api/#server())。

設(shè)置 path

設(shè)置被 socket.io 處理的路徑,默認為 /socket.io。如果需要修改,可以修改下面的配置:

export default {
  path: "/other_path"
}

注: 服務端修改了處理的路徑后,客戶端也要作對應的修改。

設(shè)置 adapter

使用多節(jié)點來部署 WebSocket 時,多節(jié)點之間可以借助 Redis 進行通信,這時可以設(shè)置 adapter 來實現(xiàn)。

import redis from "socket.io-redis";

export default {
  adapter: function(){
    return redis({ host: "localhost", port: 6379 });
  }
}

具體請見 http://socket.io/docs/using-multiple-nodes/。

socket.io client

瀏覽器端需要引入 socket.io client,下載地址為:http://socket.io/download/。

var socket = io("http://localhost:8360");
//發(fā)送事件
socket.emit("name", "data");
//監(jiān)聽事件
socket.on("name", function(data){

})

也可以直接引入一個 CDN 地址:http://s4.qhimg.com/static/535dde855bc726e2/socket.io-1.2.0.js

校驗用戶登錄

WebSocket 建立連接時可以拿到 cookie,所以可以在 open 對應的 Action 里校驗用戶是否登錄。如:

export default class extends think.controller.base {
  * openAction(){
    let userInfo = yield this.session("userInfo");
    if(think.isEmpty(userInfo)){

    }
  }
}

聊天代碼示例

聊天示例代碼請見:https://github.com/75team/thinkjs2-demos/tree/master/websocket-socket.io。

SockJS

配置

使用 SockJS 庫,需要將配置里的 type 修改為 sockjs,如:

export default {
  type: "sockjs"
}

sockjs 對象

Action 里可以通過 this.http.sockjs 拿到 sockjs 對象,該對象為 SockJS 類的一個實例。

設(shè)置 path

設(shè)置被 SockJS 處理的路徑,默認為 /sockjs,可以通過下面的配置修改:

export default {
  path: "/websocket"
}

SockJS client

瀏覽器端需要引入 SockJS client,下載地址為:https://github.com/sockjs/sockjs-client。

SockJS client 并沒有做什么封裝,所以需要額外做一層包裝,變成事件的方式,以便跟包裝后的服務端對應。包裝方式參考如下:

SockJS.prototype.emit = function(event, data){
    this.send(JSON.stringify({event: event, data: data}));
  }
SockJS.prototype.events = {};
SockJS.prototype.on = function(event, callback){
  if(!(event in this.events)){
    this.events[event] = [];
  }
  this.events[event].push(callback);
}
SockJS.prototype.onmessage = function(e) {
  var data = JSON.parse(e.data);
  var callbacks = this.events[data.event] || [];
  callbacks.forEach(function(callback){
    callback && callback(data.data);
  })
};
SockJS.prototype.onopen    = function()  {
  this.onmessage(JSON.stringify({data: {event: "open"}}));
};
SockJS.prototype.onclose   = function()  {
  this.onmessage(JSON.stringify({data: {event: "close"}}));
};

通過上面的包裝后就可以通過事件的方式來接收和發(fā)送消息了,如:

var socket = new SockJS("/sockjs"); //這里的路徑必須和配置里相同,如果沒有配置則為 /sockjs
//監(jiān)聽事件
socket.on("add user", function(data){

});
//發(fā)送事件
socket.emit("new message", "xxx");

校驗用戶登錄

SockJS 為了安全,在建立連接時不提供相關(guān)的 cookie,所以無法通過 cookie 來校驗用戶是否登錄??梢韵仍陧撁胬镙敵鲆粋€ token,建立連接時將該 token 發(fā)送用來校驗是否已經(jīng)登錄。具體請見:https://github.com/sockjs/sockjs-node#authorisation

聊天代碼示例

聊天示例代碼請見:https://github.com/75team/thinkjs2-demos/tree/master/websocket-sockjs。

nginx 反向代理

nginx 從 1.3.13 版本開始支持反向代理 WebSocket 請求,如果在項目中使用,需要在 nginx 配置文件中添加如下的配置:

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

: 使用 thinkjs 命令創(chuàng)建項目時,會自動創(chuàng)建 nginx 配置文件,并且配置文件已經(jīng)包含了上面 2 個配置,可以直接使用。

nginx 代理 WebSocket 請求的文檔請見 http://nginx.org/en/docs/http/websocket.html。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號