WebSocket 功能默认是关闭的,项目如果需要开启,可以修改配置文件 src/common/config/websocket.js

    需要将配置 on 的值修改为 true,并重启 Node.js 服务。

    事件到 Action 的映射

    ThinkJS 里对 WebSocket 的包装遵循了 socket.io 的机制,服务端和客户端之间通过事件来交互,这样服务端需要将事件名映射到对应的 Action,才能响应具体的事件。配置在 messages 字段,具体如下:

    1. export default {
    2. messages: {
    3. open: 'home/socketio/open', // WebSocket 建立连接时处理的 Action
    4. close: 'home/socketio/close', // WebSocket 关闭时处理的 Action
    5. adduser: 'home/socketio/adduser', //adduser 事件处理的 Action
    6. }
    7. }

    其中 openclose 事件名固定,表示建立连接和断开连接的事件,其他事件均为自定义,项目里可以根据需要添加。

    通过上面配置事件到 Action 的映射后,就可以在对应的 Action 作相应的处理。如:

    1. export default class extends think.controller.base {
    2. /**
    3. * WebSocket 建立连接时处理
    4. * @param {} self []
    5. * @return {} []
    6. */
    7. openAction(self){
    8. var socket = self.http.socket;
    9. this.broadcast('new message', {
    10. username: socket.username,
    11. });
    12. }
    13. }

    emit

    Action 里可以通过 this.emit 方法给当前 socket 发送事件,如:

    1. export default class extends think.controller.base {
    2. /**
    3. * WebSocket 建立连接时处理
    4. * @param {} self []
    5. * @return {} []
    6. */
    7. openAction(self){
    8. var socket = self.http.socket;
    9. this.emit('new message', 'connected');
    10. }
    11. }

    broadcast

    Action 里可以通过 方法给所有的 socket 广播事件,如:

    1. export default class extends think.controller.base {
    2. chatAction(self){
    3. var socket = self.http.socket;
    4. //广播给除当前 socket 之外的所有 sockets
    5. this.broadcast('new message', {msg: 'message', username: 'xxx'});
    6. }
    7. }

    :broadcast 方法默认是給除去当前 socket 的所有 sockets 发送事件,如果想包含当前的 socket,可以设置第三个参数值为 true

    socket 对象

    Action 里可以通过 this.http.socket 拿到当前的 socket 对象。

    事件数据

    Action 里可以通过 this.http.data 拿到发送过来事件的数据。

    socket.io

    io 对象

    在 Action 里可以通过 this.http.io 来获取 io 对象,该对象为 socket.io 的一个实例。

    io 对象包含的方法请见 http://socket.io/docs/server-api/#server())。

    设置 path

    设置被 socket.io 处理的路径,默认为 /socket.io。如果需要修改,可以修改下面的配置:

    1. export default {
    2. path: '/other_path'
    3. }

    :服务端修改了处理的路径后,客户端也要作对应的修改。

    设置 adapter

    使用多节点来部署 WebSocket 时,多节点之间可以借助 Redis 进行通信,这时可以设置 adapter 来实现。

    1. import redis from 'socket.io-redis';
    2. export default {
    3. adapter: function(){
    4. return redis({ host: 'localhost', port: 6379 });
    5. }
    6. }

    具体请见 。

    socket.io client

    浏览器端需要引入 socket.io client,下载地址为:。

    1. var socket = io('http://localhost:8360');
    2. //发送事件
    3. socket.emit('name', 'data');
    4. //监听事件
    5. socket.on('name', function(data){
    6. })

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

    校验用户登录

    WebSocket 建立连接时可以拿到 cookie,所以可以在 open 对应的 Action 里校验用户是否登录。如:

    1. export default class extends think.controller.base {
    2. let userInfo = await this.session('userInfo');
    3. if(think.isEmpty(userInfo)){
    4. }
    5. }

    聊天代码示例

    聊天示例代码请见:。

    配置

    sockjs 对象

    Action 里可以通过 this.http.sockjs 拿到 sockjs 对象,该对象为 SockJS 类的一个实例。

    设置 path

    设置被 SockJS 处理的路径,默认为 /sockjs,可以通过下面的配置修改:

    1. export default {
    2. path: '/websocket'
    3. }

    SockJS client

    浏览器端需要引入 SockJS client,下载地址为:https://github.com/sockjs/sockjs-client

    SockJS client 并没有做什么封装,所以需要额外做一层包装,变成事件的方式,以便跟包装后的服务端对应。包装方式参考如下:

    1. SockJS.prototype.emit = function(event, data){
    2. this.send(JSON.stringify({event: event, data: data}));
    3. }
    4. SockJS.prototype.events = {};
    5. SockJS.prototype.on = function(event, callback){
    6. if(!(event in this.events)){
    7. this.events[event] = [];
    8. }
    9. this.events[event].push(callback);
    10. }
    11. SockJS.prototype.onmessage = function(e) {
    12. var data = JSON.parse(e.data);
    13. var callbacks = this.events[data.event] || [];
    14. callbacks.forEach(function(callback){
    15. callback && callback(data.data);
    16. })
    17. };
    18. SockJS.prototype.onopen = function() {
    19. this.onmessage(JSON.stringify({data: {event: 'open'}}));
    20. };
    21. SockJS.prototype.onclose = function() {
    22. this.onmessage(JSON.stringify({data: {event: 'close'}}));
    23. };

    通过上面的包装后就可以通过事件的方式来接收和发送消息了,如:

    1. var socket = new SockJS('/sockjs'); //这里的路径必须和配置里相同,如果没有配置则为 /sockjs
    2. //监听事件
    3. socket.on('add user', function(data){
    4. });
    5. //发送事件
    6. socket.emit('new message', 'xxx');

    校验用户登录

    SockJS 为了安全,在建立连接时不提供相关的 cookie,所以无法通过 cookie 来校验用户是否登录。可以先在页面里输出一个 token,建立连接时将该 token 发送用来校验是否已经登录。具体请见:https://github.com/sockjs/sockjs-node#authorisation

    聊天代码示例

    聊天示例代码请见:https://github.com/75team/thinkjs2-demos/tree/master/websocket-sockjs

    nginx 反向代理

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

    1. proxy_set_header Connection "upgrade";

    : 使用 thinkjs 命令创建项目时,会自动创建 nginx 配置文件,并且配置文件已经包含了上面 2 个配置,可以直接使用。

    nginx 代理 WebSocket 请求的文档请见 http://nginx.org/en/docs/http/websocket.html