WebSocket is usually used to implement various functions such as chatroom. ThinkJS supports a lot of WebSocket libraries, for instance, socket.io
,sockjs
etc. Further more, by give a simple encapsulation to these libraries, ThinkJS provided us consistent interfaces.
WebSocket is closed by default. You can edit src/common/config/websocket.js
to open it:
export default {
on: false, // whether open WebSocket
type: 'socket.io', // the WebSocket library name, defaults to socket.io
allow_origin: '', // origin allowed
adp: undefined, // store adapter for socket,used in socket.io
path: '', // url path for websocket
messages: {
// open: 'home/websocket/open',
}
};
Change the on
field to true
, and restart Node.js.
The encapsulation to WebSocket obeyed to the socket.io
mechanism. The server and client communicate each other through events. So the server need map events to actions in order to response correctly.The configuration is specified in messages
field as following:
export default {
messages: {
open: 'home/socketio/open', // works on Websocket connected.
close: 'home/socketio/close', // works on Websocket closed.
adduser: 'home/socketio/adduser', // works when adduser.
}
}
The events name open
, close
are immutable, representing a connection or disconnection. Others can be custom, you can add according to your need.
Then, you can work with action like following code after finished above configuration.
export default class extends think.controller.base {
/**
* works on Websocket connected
* @param {} self []
* @return {} []
*/
openAction(self){
var socket = self.http.socket;
this.broadcast('new message', {
username: socket.username,
message: self.http.data
});
}
}
You can emit event to the current socket in Action through this.emit
:
export default class extends think.controller.base {
/**
* works on Websocket connected
* @param {} self []
* @return {} []
*/
openAction(self){
var socket = self.http.socket;
this.emit('new message', 'connected');
}
}
You can broadcast event to all sockets in Action through method this.broadcast
:
export default class extends think.controller.base {
chatAction(self){
var socket = self.http.socket;
// broadcast to all sockets excepting the current.
this.broadcast('new message', {msg: 'message', username: 'xxx'});
}
}
Note
: the broadcase method broadcast to all sockets except current one. You can set the third parameter to true
to include current one.
export default class extends think.controller.base {
chatAction(self){
var socket = self.http.socket;
// broadcast to all sockets including the current.
this.broadcast('new message', {msg: 'message', username: 'xxx'}, true);
}
}
You can get socket object in Action through this.http.socket
.
You can get the event data in Action through this.http.data
.
The socket.io
library encapsulates socket both front end and back end, it is very convenient to use.
You can get the io
object in Action through this.http.io
.It is an instance of socket.io
To know methods in io object, please see also http://socket.io/docs/server-api/#server()。
The socket.io process path is /socket.io
by default. You can edit the folloing configuration if you need.
/* src/common/config/websocket.js */
export default {
type: 'socket.io',
adapter: {
'socket.io': {
path: '/other_path'
}
}
}
Note
:After the server has modified the path, the client also should make the corresponding modification
When using multiple nodes to deploy WebSocket, multiple nodes can communicate with Redis. You can get things done by set up adapter.
/* src/common/config/websocket.js */
import redis from 'socket.io-redis';
export default {
type: 'socket.io',
adapter: {
'socket.io': {
adp: function(){
return redis({ host: 'localhost', port: 6379 });
}
}
}
}
See also http://socket.io/docs/using-multiple-nodes/ for more detail.
In browser end, you should introduce socket.io client. The download path is:http://socket.io/download/。
var socket = io('http://localhost:8360');
// emit event
socket.emit('name', 'data');
// listen event
socket.on('name', function(data){
})
This CDN url is available:http://s4.qhimg.com/static/535dde855bc726e2/socket.io-1.2.0.js。
Websocket can get cookie when connected. So, you can check if the user is logged in in the open
Action. For example:
export default class extends think.controller.base {
async openAction(){
let userInfo = await this.session('userInfo');
if(think.isEmpty(userInfo)){
}
}
}
See also https://github.com/75team/thinkjs2-demos/tree/master/websocket-socket.io for more detailed chat code.
You should edit the type
field in the configuration to sockjs
:
export default {
type: 'sockjs'
}
You can get sockjs object through this.http.sockjs
in Action. The object is an instance of SocketJS.
The SocketJS process path is /sockjs
by default.You can edit the folloing configuration if you need change.
export default {
path: '/websocket'
}
In Browser end, you should introduce SockJS client. The download path is:https://github.com/sockjs/sockjs-client。
SockJS client does not do too much encapsulation, so you need encapsulate it by yourself, change it to the event way, in order to follow the server side. The encapsulate method is as follows:
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'}}));
};
After do above, we can receive and emit message, for example:
var socket = new SockJS('/sockjs'); // this path must be same with configuration.Defaults to /sockjs
// listen event
socket.on('add user', function(data){
});
// emit event
socket.emit('new message', 'xxx');
For the safety reason, the SockJS doesn't supply cookie. So you can't check if the user is logined through cookie. You can output a token in your page, then send the token when connected to check.See also https://github.com/sockjs/sockjs-node#authorisation for more details.
See also https://github.com/75team/thinkjs2-demos/tree/master/websocket-sockjsfor more detailed chat code
From the 1.3.13
version, Nginx supports reverse proxy WebSocket request, if used in the project, you need to add the following configuration in the nginx configuration file:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Note
: when using thinkjs
command to create project, ThinkJS creats nginx configuration file ,including these two configuration fields. You can use it directly.
Please visit http://nginx.org/en/docs/http/websocket.html to read the reverse proxy WebSocket request document.
You can get an array of WebSocket connecting objects by using thinkCache(thinkCache.WEBSOCKET)
.
ThinkJS has not supported private chat yet, you can implement it by first find all of the WebSocket connections, and then match the corresponding connections.