快速开始

AdonisJs 提供强大的 WebSocket Provider 来提供实时应用程序。

服务器在纯 WebSocket 连接上工作(由所有主流浏览器支持),并在 Node.js 进程集群中自然扩展。

建立

由于默认情况下未安装 WebSocket Provider ,请运行 npm 安装:

adonis install @adonisjs/websocket

安装提供程序会将以下文件添加到项目中:

  • config/socket.js 包含 WebSocket 服务器配置。

  • start/socket.js 引导 WebSocket 服务器并注册通道。

  • start/wsKernel.js 注册中间件以在通道订阅上执行。

接下来,在 start/app.js 文件中注册提供程序:

const providers = [
  '@adonisjs/websocket/providers/WsProvider'
]

最后,指示器 Ignitor 在根 server.js 文件中引导 WebSocket 服务器:

const { Ignitor } = require('@adonisjs/ignitor')

new Ignitor(require('@adonisjs/fold'))
   .appRoot(__dirname)
   .wsServer() // boot the WebSocket server
   .fireHttpServer()
   .catch(console.error)

集群支持

运行 Node.js 集群时,主节点需要连接工作节点之间的发布/订阅通信。

为此,请将以下代码添加到根 server.js 文件的顶部:

const cluster = require('cluster')

if (cluster.isMaster) {
  for (let i=0; i < 4; i ++) {
    cluster.fork()
  }
  require('@adonisjs/websocket/clusterPubSub')()
  return
}

基本例子

让我们构建一个用于用户消息传递的单聊天服务器。

为了简单起见,我们不会存储用户消息,只是提供它们。

打开 start/socket.js 文件并粘贴以下代码:

const Ws = use('Ws')

Ws.channel('chat', 'ChatController')

我们也可以将闭包绑定到 Ws.channel 方法,但建议使用专用控制器。 接下来,ChatController 使用以下 make:controller 命令创建:

adonis make:controller Chat --type=ws
# 输出
create  app/Controllers/Ws/ChatController.js
'use strict'

class ChatController {
  constructor ({ socket, request }) {
    this.socket = socket
    this.request = request
  }
}

module.exports = ChatController

客户端代码

让我们从服务器切换到客户端并订阅该 chat 频道。

首先,将 CSS 和 HTML 模板从此要点复制到以下位置:

  • CSS → public/style.css

  • HTML 模板 → resources/views/chat.edge

确保定义提供 HTML 模板的路由。 接下来,创建一个 public/chat.js 文件并粘贴下面的代码以将客户端连接到服务器(为了简单起见,我们使用 jQuery ):

let ws = null

$(function () {
  // Only connect when username is available
  if (window.username) {
    startChat()
  }
})

function startChat () {
  ws = adonis.Ws().connect()

  ws.on('open', () => {
    $('.connection-status').addClass('connected')
    subscribeToChannel()
  })

  ws.on('error', () => {
    $('.connection-status').removeClass('connected')
  })
}

然后,添加通道订阅方法,绑定侦听器来处理消息:

// ...

function subscribeToChannel () {
  const chat = ws.subscribe('chat')

  chat.on('error', () => {
    $('.connection-status').removeClass('connected')
  })

  chat.on('message', (message) => {
    $('.messages').append(`
      <div class="message"><h3> ${message.username} </h3> <p> ${message.body} </p> </div>
    `)
  })
}

最后,添加事件处理程序以在 Enter 释放密钥时发送消息:

// ...

$('#message').keyup(function (e) {
  if (e.which === 13) {
    e.preventDefault()

    const message = $(this).val()
    $(this).val('')

    ws.getSubscription('chat').emit('message', {
      username: window.username,
      body: message
    })
    return
  }
})

服务器代码

现在完成了客户端,让我们切换回服务器。

将 onMessage 事件方法添加到 ChatController 文件中:

class ChatController {
  constructor ({ socket, request }) {
    this.socket = socket
    this.request = request
  }

  onMessage (message) {
    this.socket.broadcastToAll('message', message)
  }
}

在上面的示例中,onMessage 方法通过套接字 broadcastToAll 方法向所有连接的客户端发送相同的消息。

控制器

控制器通过为每个通道定义单独的类来保持代码的有序性。

WebSocket 控制器存储在 app/Controllers/Ws 目录中。

每个订阅创建一个新的控制器实例,并将一个 context 对象传递给它的构造函数,从而使 socket 实例能够解压缩,如下所示:

class ChatController {
  constructor ({ socket }) {
    this.socket = socket
  }
}

事件方法 通过创建具有相同名称的控制器方法绑定到 WebSocket 事件:

class ChatController {
  onMessage () {
    // same as: socket.on('message')
  }

  onClose () {
    // same as: socket.on('close')
  }

  onError () {
    // same as: socket.on('error')
  }
}

事件方法必须以 on 关键字为前缀。

最后一次更新: 7/4/2019, 10:38:36 PM