Socket.IO 简介
就像 HTTP 一样,WebSocket 是一种通信协议,但它在客户端和服务器之间提供全双工(双向)通信。与 HTTP 不同,WebSocket 连接始终保持打开状态以进行实时数据传输。因此,在 WebSocket 应用程序中,服务器可以在没有客户端发起请求的情况下向客户端发送数据。此外,与以 HTTP 或 HTTPS(超文本传输安全协议)开头的 HTTP 模式不同,WebSocket 协议模式以 ws 或 wss(安全 WebSocket)开头;例如:
ws://example.com:4000
Socket.IO 是一个 JavaScript 库,它使用 WebSocket 协议和轮询作为后备选项来创建实时 Web 应用程序。它支持任何平台、浏览器或设备,并处理服务器和客户端的所有降级,以实现实时的全双工通信。如今,大多数浏览器都支持 WebSocket 协议,包括 Google Chrome、Microsoft Edge、Firefox、Safari 和 Opera。但是,在使用 Socket.IO 时,我们必须同时使用其客户端和服务器端库。客户端库在浏览器内部运行,而服务器端库在您的服务器端 Node.js 应用程序上运行。因此,让我们让这两个库在我们的应用程序中工作起来。
如果您想了解更多关于 Socket.IO 的信息,请访问 https://socket.io/。 |
添加和使用 Socket.IO 服务器和客户端
我们将把 Socket.IO 服务器添加到我们在前几节中构建的 API 中,然后最终将 Socket.IO 客户端添加到 Nuxt 应用程序中。但在将其添加到 Nuxt 应用程序之前,我们会将其添加到一个简单的 HTML 页面,以便我们对 Socket.IO 服务器和 Socket.IO 客户端如何协同工作有一个大致的了解。让我们学习如何做到这一点:
-
通过 npm 安装 Socket.IO 服务器:
$ npm i socket.io
-
如果尚未创建,请在 /configs/ 目录中创建一个 index.js 文件来存储服务器设置:
// configs/index.js export default { server: { port: 4000 }, }
通过这个简单的设置,我们的 API 将在 4000 端口上提供服务。
-
导入 socket.io 并使用 Koa 的新实例将其绑定到 Node.js HTTP 对象,以创建 Socket.IO 的新实例,如下所示:
// backend/koa/public/index.js import Koa from 'koa' import socket from 'socket.io' import http from 'http' import config from 'Configs' import middlewares from '../middlewares' const app = new Koa() const host = process.env.HOST || '127.0.0.1' const port = process.env.PORT || config.server.port middlewares(app) const server = http.createServer(app.callback()) const io = socket(server) io.sockets.on('connection', socket => { console.log('a user connected: ' + socket.id) socket.on('disconnect', () => { console.log('user disconnected: ' + socket.id) }) }) server.listen(port, host)
创建 Socket.IO 的新实例后,我们可以开始监听来自 socket 回调的传入 socket 的 Socket.IO 连接事件。我们将传入的 socket 及其 ID 记录到控制台。当传入的 socket 断开连接时,我们还会记录其断开连接事件。最后,请注意我们使用原生的 Node.js HTTP 在 localhost:4000 上启动并提供应用程序服务,而不是像以前那样使用 Koa 内部的 HTTP:
// 以前的做法 // app.listen(4000)
-
创建一个 socket-client.html 页面,并通过 CDN 导入 Socket.IO 客户端。通过传递 localhost:4000 作为特定 URL 来创建它的新实例,如下所示:
// frontend/html/socket-client.html <script src="https://cdn.jsdelivr.net/npm/socket.io-client@2/dist/socket.io.js"></script> <script> var socket = io('http://localhost:4000/') </script>
现在,如果您在浏览器上浏览此 HTML 页面,或者刷新该页面时,您应该会在控制台中看到打印的带有 socket ID 的日志,如下所示:
a user connected: abeGnarBnELo33vQAAAB
当您关闭 HTML 页面时,您还应该在控制台中看到打印的带有 socket ID 的日志,如下所示:
user disconnected: abeGnarBnELo33vQAAAB
这就是连接 Socket.IO 的服务器端和客户端所需的全部操作。这非常简单容易,不是吗?但是我们在这里所做的只是连接和断开服务器和客户端。我们需要更多——我们希望同时传输数据。为此,我们只需要相互发送和接收事件,我们将在接下来的步骤中进行操作。
如果您想使用 Socket.IO 客户端的本地版本,可以将脚本标签的 URL 源指向 /node_modules/socket.io-client/dist/socket.io.js。
-
通过使用 Socket.IO 服务器的
emit
方法从服务器创建一个发送事件,如下所示:// backend/koa/public/index.js io.sockets.on('connection', socket => { io.emit('emit.onserver', 'Hi client, what you up to?') console.log('Message to client: ' + socket.id) })
在这里,您可以看到我们通过名为
emit.onserver
的自定义事件发送了一个简单的消息,并将活动记录到控制台。请注意,我们只能在连接建立后发送事件。然后,我们可以在客户端监听这个自定义事件并记录来自服务器的消息,如下所示:// frontend/html/socket-client.html <script> socket.on('emit.onserver', function (message) { console.log('Message from server: ' + message) }) </script>
-
所以,现在,如果您再次刷新浏览器上的页面,您应该会在控制台中看到打印的日志,如下所示:
Message to client: abeGnarBnELo33vQAAAB // 服务器端 Message from server: Hi client, what you up to? // 客户端
-
通过使用
Socket.IO
客户端的emit
方法从客户端创建一个发送事件,如下所示:// frontend/html/socket-client.html <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha256-pasqAKBDmFT4eHoN2ndd6lN370kFiGUFyTiUHWhU7k8=" crossorigin="anonymous"></script> <button class="button-sent">Send</button> <script> $('.button-sent').click(function(e){ e.preventDefault() var message = 'Hi server, how are you holding up?' socket.emit('emit.onclient', message) console.log('Message sent to server.') return false }) </script>
在这里,您可以看到,首先,我们通过 CDN 安装了 jQuery 并创建了一个带有 jQuery 点击事件的 <button>。其次,当按钮被点击时,我们使用名为 emit.onclient 的 Socket.IO 自定义事件发送了一个简单的消息。最后,我们将活动记录到控制台。
-
之后,我们可以在服务器端监听 Socket.IO 自定义事件并记录来自客户端的消息,如下所示:
// backend/koa/public/index.js socket.on('emit.onclient', (message) => { console.log('Message from client, '+ socket.id + ' :' + message); })
-
如果您再次刷新浏览器上的页面,您应该会在控制台中看到打印的日志以及 socket ID,如下所示:
Message sent to server. // 客户端 Message from client, abeGnarBnELo33vQAAAB: Hi server, how are you holding up? // 服务器端
您现在知道如何使用 Socket.IO 实时地来回传输数据——只需发送自定义事件并监听它们即可。接下来您应该了解的是如何将 Socket.IO 与 RethinkDB 中的变更提要集成,以便将来自数据库的实时数据传输到客户端。所以,请继续阅读!
集成 Socket.IO 服务器和 RethinkDB changefeeds
您可能还记得,您之前使用位于 localhost:8080/#dataexplorer 的管理 UI 中的数据浏览器再次摆弄过 RethinkDB 的变更提要。要订阅变更提要,您只需要将 ReQL 的 changes 命令链接到查询,如下所示:
r.db('nuxtdb').table('user').changes()
RethinkDB 的变更提要包含从 RethinkDB 数据库实时发送到我们 API 的数据,这意味着我们需要在服务器端使用 Socket.IO 服务器捕获这些提要,并将它们发送到客户端。因此,让我们学习如何通过重构我们在本章中一直在开发的 API 来捕获它们:
-
通过 npm 将 Socket.IO 服务器安装到您的 API 中:
$ npm i socket.io
-
在 /core/ 目录中的 changefeeds.js 文件中创建一个具有以下代码的异步匿名箭头函数:
// core/database/rethinkdb/changefeeds.js import rdb from 'rethinkdb' import rdbConnection from './connection' export default async (io, tableName, eventName) => { try { const connection = await rdbConnection() var cursor = await rdb.table(tableName) .changes() .run(connection) cursor.each(function (err, row) { if (err) { throw err } io.emit(eventName, row) }) } catch( err ) { console.error(err); } }
在此函数中,我们将 rethinkdb 导入为 rdb,并将我们的 RethinkDB 数据库连接导入为 rdbConnection,然后使用以下项作为此函数的参数:
-
Socket.IO 服务器的实例
-
您想要使用的 Socket.IO 发送自定义事件名称
-
您想要订阅其变更提要的 RethinkDB 表名
变更提要将以游标对象的形式返回文档作为回调,因此我们遍历游标对象并使用自定义事件名称发送文档的每一行。
-
-
在 /public/ 目录中的应用程序根目录中导入 changefeeds 函数作为 rdbChangeFeeds,并将其与 index.js 文件中的其余现有代码集成,如下所示:
// public/index.js import Koa from 'koa' import socket from 'socket.io' import http from 'http' import config from 'Configs' import middlewares from '../middlewares' import rdbChangeFeeds from 'Core/database/rethinkdb/changefeeds' const app = new Koa() const host = process.env.HOST || '127.0.0.1' const port = process.env.PORT || config.server.port middlewares(app) const server = http.createServer(app.callback()) const io = socket(server) io.sockets.on('connection', socket => { //... }) rdbChangeFeeds(io, 'user', 'user.changefeeds') server.listen(port, host)
在上面的代码中,我们要订阅的表名是 user,我们要调用的发送事件名称是 user.changefeeds。因此,我们将它们与 socket.io 实例一起传递给 rdbChangeFeeds 函数。这就是您只需要做的一次性全局集成 Socket.IO 和 RethinkDB 的全部操作。
做得好!您已成功地在服务器端集成了 Koa、RethinkDB 和 Socket.IO,并创建了一个实时 API。但是客户端呢,我们如何监听从 API 发送的事件?我们将在下一节中找到答案。