ThinkJS currently supports socket.io
for WebSocket and has some simple packaging for it, with subsequent additions of socketjs, ws Library support.
In a clustered environment, WebSocket requires a sticky session to ensure that a given client requests that the same worker be hit, otherwise the handshake mechanism will not work. In order to do this, you need to turn on the stickyCluster
configuration.
In order to ensure the performance, stickyCluster
function is disabled by default. If the project needs to be enabled, you can modify the configuration file src/config/config.js
:
module.exports = {
stickyCluster: true,
// ...
};
WebSocket is integrated into ThinkJS as extend
, with src/config/extend.js
first configured:
const websocket = require('think-websocket');
module.exports = [
// ...
websocket(think.app),
];
Each implementation of WebSocket exists as an adapter
, taking socket.io
as an example (encapsulated with think-websocket-socket.io), configured in src/config/adapter.js
as follows:
const socketio = require('think-websocket-socket.io');
exports.websocket = {
type: 'socketio',
common: {
// common config
},
socketio: {
handle: socketio,
allowOrigin: '127.0.0.1:8360', // all domains are allowed by default
path: '/socket.io', // '/socket.io' as default
adapter: null, // no adapter by default
messages: [{
open: '/websocket/open',
addUser: '/websocket/addUser'
}]
}
}
Taking socket.io
as an example, ThinkJS follows the mechanism of interaction between the server and client of socket.io
through an event, so the server needs to map the event name to the corresponding Action in order to respond to the specific event. The mapping of events is configured in the messages
field as follows:
exports.websocket = {
// ...
socketio: {
// ...
messages: {
open: '/websocket/open', // when the connection is established, the open Action under the corresponding websocket Controller is processed
close: '/websocket/close', // Action to close when closing the connection
addUser: '/websocket/addUser', // Action for addUser event handling
}
}
}
The open
and close
event names are fixed, which means that the connection is established and disconnected. Other events are self-defined, and can be added as needed in the project.
By configuring the mapping of the event to the Action, you can do the corresponding processing in the corresponding Action. Such as:
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('get the data sent by the client addUser event', this.wsData);
console.log('get the current WebSocket object', this.websocket);
console.log('determine if the current request is a WebSocket request', this.isWebsocket);
}
}
Action can send the current socket
event by this.emit
method, such as:
module.exports = class extends think.Controller {
constructor(...arg) {
super(...arg);
}
openAction() {
this.emit('opend', 'This client opened successfully!')
}
}
Action can give all the socket
broadcast events by this.broadcase
method, such as:
module.exports = class extends think.Controller {
constructor(...arg) {
super(...arg);
}
openAction() {
this.broadcast('joined', 'There is a new client joined successfully!')
}
}
Client sample code is as follows:
<script src="http://lib.baomitu.com/socket.io/2.0.1/socket.io.js"></script>
<script type="text/javascript">
var socket = io('http://localhost:8360');
$('.send').on('click', function(evt) {
var username = $.trim($('.usernameInput').val());
if(username) {
socket.emit('addUser', username);
}
});
socket.on('opend', function(data) {
console.log('opend:', data);
});
socket.on('joined', function(data) {
console.log('joined:', data);
});
</script>
socket.io
wraps both front and back of WebSocket and is very convenient to use.
In Action, you can get io
object via this.ctx.req.io
, which is an instance of socket.io
.
io object contains the method see the document https://socket.io/docs/server-api/#server。
Set the path to be handled by socket.io
, which defaults to /socket.io
. If you need to change, you can modify the configuration of src/config/adapter.js
:
exports.websocket = {
// ...
socketio: {
// ...
path: '/socket.io',
}
}
For the detailed configuration of path, see the document https://socket.io/docs/server-api/#server-path-value. Note that if the server modifies the processing path, the client also needs to make corresponding changes.
By default, socket.io
allows all domain names to be accessed. If you need to change, you can modify the configuration of src/config/adapter.js
:
exports.websocket = {
// ...
socketio: {
// ...
allowOrigin: '127.0.0.1:8360',
}
}
For the detailed configuration of allowOrigin, see the document https://socket.io/docs/server-api/#server-origins-value. Note that if the server modifies the processing path, the client also needs to make corresponding changes.
When using multi-node to deploy WebSocket, multiple nodes can communicate with Redis, then you can set the adapter to achieve.
const redis = require('socket.io-redis');
exports.websocket = {
// ...
socketio: {
// ...
adapter: redis,
}
}
For the detailed configuration of adapter, see the document https://socket.io/docs/server-api/#server-adapter-value。