[TOC=2,3]
項(xiàng)目里經(jīng)常要要使用 WebSocket 來(lái)實(shí)現(xiàn)聊天等功能,ThinkJS 支持多種 WebSocket 庫(kù),如:`socket.io`,`sockjs`?等,并對(duì)這些庫(kù)進(jìn)行了一些簡(jiǎn)單的包裝,讓使用的接口一致。
## 開啟 WebSocket
WebSocket 功能默認(rèn)是關(guān)閉的,項(xiàng)目如果需要開啟,可以修改配置文件?`src/common/config/websocket.js`:
~~~
export default {
on: false, //是否開啟 WebSocket
type: "socket.io", //使用的 WebSocket 庫(kù)類型,默認(rèn)為 socket.io
allow_origin: "", //允許的 origin
adapter: undefined, // socket 存儲(chǔ)的 adapter,socket.io 下使用
path: "", //url path for websocket
messages: {
// open: "home/websocket/open",
}
};
~~~
需要將配置?`on`?的值修改為 true,并重啟 Node.js 服務(wù)。
## 事件到 Action 的映射
ThinkJS 里對(duì) WebSocket 的包裝遵循了?`socket.io`?的機(jī)制,服務(wù)端和客戶端之間通過(guò)事件來(lái)交互,這樣服務(wù)端需要將事件名映射到對(duì)應(yīng)的 Action,才能響應(yīng)具體的事件。配置在?`messages`?字段,具體如下:
~~~
export default {
messages: {
open: "home/socketio/open", // WebSocket 建立連接時(shí)處理的 Action
close: "home/socketio/close", // WebSocket 關(guān)閉時(shí)處理的 Action
adduser: "home/socketio/adduser", //adduser 事件處理的 Action
}
}
~~~
其中?`open`?和?`close`?事件名固定,表示建立連接和斷開連接的事件,其他事件均為自定義,項(xiàng)目里可以根據(jù)需要添加。
## Action 處理
通過(guò)上面配置事件到 Action 的映射后,就可以在對(duì)應(yīng)的 Action 作相應(yīng)的處理了。如:
~~~
export default class extends think.controller.base {
/**
* WebSocket 建立連接時(shí)處理
* @param {} self []
* @return {} []
*/
openAction(self){
var socket = self.http.socket;
this.broadcast("new message", {
username: socket.username,
message: self.http.data
});
}
}
~~~
### emit
Action 里可以通過(guò)?`this.emit`?方法給當(dāng)前 socket 發(fā)送事件,如:
~~~
export default class extends think.controller.base {
/**
* WebSocket 建立連接時(shí)處理
* @param {} self []
* @return {} []
*/
openAction(self){
var socket = self.http.socket;
this.emit("new message", "connected");
}
}
~~~
### broadcast
Action 里可以通過(guò)?`this.broadcast`?方法給所有的 socket 廣播事件,如:
~~~
export default class extends think.controller.base {
chatAction(self){
var socket = self.http.socket;
//廣播給除當(dāng)前 socket 之外的所有 sockets
this.broadcast("new message", {msg: "message", username: "xxx"});
}
}
~~~
`注:`?broadcast 方法默認(rèn)是給除去當(dāng)前 socket 的所有 sockets 發(fā)送事件,如果想包含當(dāng)前的 socket,可以設(shè)置第三個(gè)參數(shù)值為?`true`。
~~~
export default class extends think.controller.base {
chatAction(self){
var socket = self.http.socket;
//廣播給所有的 sockets,包含當(dāng)前的 socket
this.broadcast("new message", {msg: "message", username: "xxx"}, true);
}
}
~~~
### socket 對(duì)象
Action 里可以通過(guò)?`this.http.socket`?拿到當(dāng)前的 socket 對(duì)象。
### 事件數(shù)據(jù)
Action 里可以通過(guò)?`this.http.data`?拿到發(fā)送過(guò)來(lái)事件的數(shù)據(jù)。
## socket.io
`socket.io`?對(duì) WebSocket 前后端都有封裝,使用起來(lái)非常方便。
### io 對(duì)象
在 Action 里可以通過(guò)?`this.http.io`?來(lái)獲取?`io`?對(duì)象,該對(duì)象為 socket.io 的一個(gè)實(shí)例。
io 對(duì)象包含的方法請(qǐng)見?[http://socket.io/docs/server-api/#server()](http://socket.io/docs/server-api/#server())。
### 設(shè)置 path
設(shè)置被 socket.io 處理的路徑,默認(rèn)為?`/socket.io`。如果需要修改,可以修改下面的配置:
~~~
export default {
path: "/other_path"
}
~~~
`注:`?服務(wù)端修改了處理的路徑后,客戶端也要作對(duì)應(yīng)的修改。
### 設(shè)置 adapter
使用多節(jié)點(diǎn)來(lái)部署 WebSocket 時(shí),多節(jié)點(diǎn)之間可以借助 Redis 進(jìn)行通信,這時(shí)可以設(shè)置 adapter 來(lái)實(shí)現(xiàn)。
~~~
import redis from "socket.io-redis";
export default {
adapter: function(){
return redis({ host: "localhost", port: 6379 });
}
}
~~~
具體請(qǐng)見?[http://socket.io/docs/using-multiple-nodes/](http://socket.io/docs/using-multiple-nodes/)。
### socket.io client
瀏覽器端需要引入 socket.io client,下載地址為:[http://socket.io/download/](http://socket.io/download/)。
~~~
var socket = io("http://localhost:8360");
//發(fā)送事件
socket.emit("name", "data");
//監(jiān)聽事件
socket.on("name", function(data){
})
~~~
也可以直接引入一個(gè) CDN 地址:[http://s4.qhimg.com/static/535dde855bc726e2/socket.io-1.2.0.js](http://s4.qhimg.com/static/535dde855bc726e2/socket.io-1.2.0.js)。
### 校驗(yàn)用戶登錄
WebSocket 建立連接時(shí)可以拿到 cookie,所以可以在?`open`?對(duì)應(yīng)的 Action 里校驗(yàn)用戶是否登錄。如:
~~~
export default class extends think.controller.base {
* openAction(){
let userInfo = yield this.session("userInfo");
if(think.isEmpty(userInfo)){
}
}
}
~~~
### 聊天代碼示例
聊天示例代碼請(qǐng)見:[https://github.com/75team/thinkjs2-demos/tree/master/websocket-socket.io](https://github.com/75team/thinkjs2-demos/tree/master/websocket-socket.io)。
## SockJS
### 配置
使用 SockJS 庫(kù),需要將配置里的 type 修改為?`sockjs`,如:
~~~
export default {
type: "sockjs"
}
~~~
### sockjs 對(duì)象
Action 里可以通過(guò)?`this.http.sockjs`?拿到 sockjs 對(duì)象,該對(duì)象為 SockJS 類的一個(gè)實(shí)例。
### 設(shè)置 path
設(shè)置被 SockJS 處理的路徑,默認(rèn)為?`/sockjs`,可以通過(guò)下面的配置修改:
~~~
export default {
path: "/websocket"
}
~~~
### SockJS client
瀏覽器端需要引入 SockJS client,下載地址為:[https://github.com/sockjs/sockjs-client](https://github.com/sockjs/sockjs-client)。
SockJS client 并沒有做什么封裝,所以需要額外做一層包裝,變成事件的方式,以便跟包裝后的服務(wù)端對(duì)應(yīng)。包裝方式參考如下:
~~~
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"}}));
};
~~~
通過(guò)上面的包裝后就可以通過(guò)事件的方式來(lái)接收和發(fā)送消息了,如:
~~~
var socket = new SockJS("/sockjs"); //這里的路徑必須和配置里相同,如果沒有配置則為 /sockjs
//監(jiān)聽事件
socket.on("add user", function(data){
});
//發(fā)送事件
socket.emit("new message", "xxx");
~~~
### 校驗(yàn)用戶登錄
SockJS 為了安全,在建立連接時(shí)不提供相關(guān)的 cookie,所以無(wú)法通過(guò) cookie 來(lái)校驗(yàn)用戶是否登錄??梢韵仍陧?yè)面里輸出一個(gè) token,建立連接時(shí)將該 token 發(fā)送用來(lái)校驗(yàn)是否已經(jīng)登錄。具體請(qǐng)見:[https://github.com/sockjs/sockjs-node#authorisation](https://github.com/sockjs/sockjs-node#authorisation)。
### 聊天代碼示例
聊天示例代碼請(qǐng)見:[https://github.com/75team/thinkjs2-demos/tree/master/websocket-sockjs](https://github.com/75team/thinkjs2-demos/tree/master/websocket-sockjs)。
## nginx 反向代理
nginx 從?`1.3.13`?版本開始支持反向代理 WebSocket 請(qǐng)求,如果在項(xiàng)目中使用,需要在 nginx 配置文件中添加如下的配置:
~~~
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
~~~
`注`: 使用?`thinkjs`?命令創(chuàng)建項(xiàng)目時(shí),會(huì)自動(dòng)創(chuàng)建 nginx 配置文件,并且配置文件已經(jīng)包含了上面 2 個(gè)配置,可以直接使用。
nginx 代理 WebSocket 請(qǐng)求的文檔請(qǐng)見?[http://nginx.org/en/docs/http/websocket.html](http://nginx.org/en/docs/http/websocket.html)。
- 快速入門
- 介紹
- 創(chuàng)建項(xiàng)目
- 項(xiàng)目結(jié)構(gòu)
- 代碼規(guī)范
- 升級(jí)指南
- 進(jìn)階應(yīng)用
- 模塊
- 控制器
- 視圖
- 配置
- 路由
- 模型
- 介紹
- 事務(wù)
- 關(guān)聯(lián)模型
- Mysql
- MongoDB
- SQLite
- Adapter
- 介紹
- Cache
- Session
- WebSocket
- Template
- 擴(kuò)展功能
- thinkjs 命令
- 靜態(tài)資源訪問(wèn)
- Middleware
- Service
- Cookie
- 錯(cuò)誤處理
- 錯(cuò)誤信息
- 數(shù)據(jù)校驗(yàn)
- 國(guó)際化
- 路徑常量
- REST API
- 定時(shí)任務(wù)
- 線上部署
- 推薦模塊
- API
- think
- think.base
- think.http.base
- http
- controller
- rest controller
- model
- model.mongo
- middleware
