WebSocket
对于 WebSocket 目前 ThinkJS 支持了 socket.io、ws 对其进行了一些简单的包装。
开启 WebSocket
// socket.io 参考 https://thinkjs.org/zh-cn/doc/3.0/websocket.html
在集群环境中,WebSocket 要求使用粘性会话,来确保给定客户端请求命中相同的 worker,否则其握手机制将无法正常工作。 为了实现这一点,需要开启 stickyCluster
配置。
为了保证性能,stickyCluster
功能默认是关闭的,项目如果需要开启,可以修改配置文件 src/config/config.js
:
module.exports = {
stickyCluster: true,
// ...
};
与此同时,ThinkJS将websocket
封装为think-websocket
,但是需要在项目目录下执行
$ npm install -s think-websocket
来安装模块
配置 WebSocket
WebSocket 是以 extend
的形式集成到 ThinkJS 的,首先要配置 src/config/extend.js
:
const websocket = require('think-websocket');
module.exports = [
// ...
websocket(think.app),
];
WebSocket 的各个实现是以 adapter
的形式存在的,以 socket.io
为例(使用 think-websocket-socket.io 进行了封装),在 src/config/adapter.js
中配置如下:
const socketio = require('think-websocket-socket.io');
exports.websocket = {
type: 'socketio',
common: {
// common config
},
socketio: {
handle: socketio,
// allowOrigin: '127.0.0.1:8360', // 默认所有的域名都允许访问
path: '/socket.io', // 默认 '/socket.io'
adapter: null, // 默认无 adapter
messages: {
open: '/socket/open',
addUser: '/socket/addUser'
}
}
}
事件到 Action 的映射
以 socket.io
为例,ThinkJS 遵循了 socket.io
服务端和客户端之间通过事件来交互的机制,这样服务端需要将事件名映射到对应的 Action,才能响应具体的事件。事件的映射关系配置在 messages
字段,具体如下:
exports.websocket = {
// ...
socketio: {
// ...
messages: {
open: '/socket/open', // 建立连接时处理对应到 socket Controller 下的 open Action
close: '/socket/close', // 关闭连接时处理的 Action
addUser: '/socket/addUser', // addUser 事件处理的 Action
}
}
}
其中 open
和 close
事件名固定,表示建立连接和断开连接的事件,其他事件均为自定义,项目里可以根据需要添加。
服务端 Action 处理
通过配置事件到 Action 的映射后,就可以在对应的 Action 作相应的处理。如:
src\controller\socket.js
module.exports = class extends think.Controller {
constructor(...arg) {
super(...arg);
}
openAction() {
this.emit('opend', 'This client opened successfully!')
this.broadcast('joined', 'There is a new client joined successfully!')
}
addUserAction() {
console.log('获取客户端 addUser 事件发送的数据', this.wsData);
console.log('获取当前 WebSocket 对象', this.websocket);
console.log('判断当前请求是否是 WebSocket 请求', this.isWebsocket);
}
}
emit
Action 里可以通过 this.emit
方法给当前 socket
发送事件,如:
module.exports = class extends think.Controller {
constructor(...arg) {
super(...arg);
}
openAction() {
this.emit('opend', 'This client opened successfully!')
}
}
broadcast
Action 里可以通过 this.broadcast
方法给所有的 socket
广播事件,如:
module.exports = class extends think.Controller {
constructor(...arg) {
super(...arg);
}
openAction() {
this.broadcast('joined', 'There is a new client joined successfully!')
}
}
客户端示例
客户端示例代码如下:
// websocke 参考: https://github.com/thinkjs/think-websocket-ws
ThinkJS 3.X's ws adapter for websocket.
Install
npm install think-websocket-ws --save
How to Config
WebSocket 是以 extend 的形式集成到 ThinkJS 的,首先要配置 src/config/extend.js: (同socket.io, 官方文档遗漏)
const websocket = require('think-websocket');
module.exports = [
// ...
websocket(think.app),
];
Edit config file src/config/adapter.js
, add options:
const ws = require('think-websocket-ws');
exports.websocket = {
type: 'ws',
common: {
// common config
},
ws: {
handle: ws,
path: '/ws',
messages: [{
close: '/ws/close',
open: '/ws/open',
addUser: '/ws/addUser'
}]
}
}
More options see at ws doc.
Edit config file src/config/config.js
, add options:
module.exports = {
// ...
stickyCluster: true
// ...
}
Work with Action
// server src\controller\ws.js
module.exports = class extends think.Controller {
constructor(...arg) {
super(...arg);
}
openAction() {
console.log('ws open');
return this.success();
}
closeAction() {
console.log('ws close');
return this.success();
}
addUserAction() {
console.log('addUserAction ...');
console.log(this.wsData); // this.req.websocketData, 'thinkjs'
console.log(this.websocket); // this.req.websocket, websocket instance
console.log(this.isWebsocket); // this.isMethod('WEBSOCKET'), true
return this.success();
}
}
// client
var ws = new WebSocket('ws://127.0.0.1:8360/ws');
ws.onopen = function() {
console.log('open...');
}
ws.onerror = function(evt) {
console.log(evt);
}
ws.onmessage = function(data) {
console.log(data);
}
$('.send').on('click', function(evt) {
var username = $.trim($('.usernameInput').val());
var room = $.trim($('.roomInput').val());
ws.send(JSON.stringify({
event: 'addUser',
data: {
username: username,
room: room
}
}));
});
emit
You can emit event to the current socket in Action through this.emit
:
// server
module.exports = class extends think.Controller {
constructor(...arg) {
super(...arg);
}
openAction() {
this.emit('opend', 'This client opened successfully!');
return this.success();
}
}
// client
ws.onmessage = function(data) {
data = JSON.parse(data.data);
console.log(data.event); // opend
console.log(data.data); // This client opened successfully!
}
broadcast
You can broadcast event to all sockets in Action through method this.broadcast
:
// server
module.exports = class extends think.Controller {
constructor(...arg) {
super(...arg);
}
openAction() {
this.broadcast('joined', 'There is a new client joined successfully!');
return this.success();
}
}
// client
ws.onmessage = function(data) {
data = JSON.parse(data.data);
console.log(data.event); // joined
console.log(data.data); // There is a new client joined successfully!
}
server端连接多客户端代码示例
const Base = require('./base.js');
const sockets = {};
module.exports = class extends Base {
constructor(...arg) {
super(...arg);
}
openAction() {
console.log('ws open');
this.emit('opend', 'This client opened successfully!')
this.broadcast('joined', 'There is a new client joined successfully!');
return this.success();
}
closeAction() {
console.log('ws close');
return this.success();
}
taskBeginAction() {
console.log('向客户端下达执行任务的指令');
console.log(sockets,"--------");
// http://127.0.0.1:8360/ws/taskBegin?name=555
var name = this.get("name")
console.log(name)
sockets[name].emit('taskBegin la', {'name':'task11'})
return this.success();
}
addUserAction() {
console.log('addUserAction ...');
sockets[this.wsData.data.username] = this;
// console.log(this.wsData,111); // this.req.websocketData, 'thinkjs'
// console.log(this.websocket,222); // this.req.websocket, websocket instance
// console.log(this.isWebsocket,333); // this.isMethod('WEBSOCKET'), true
return this.success();
}
};