文档阅读——y-websocket

文档阅读——y-websocket

本人项目需要使用Yjs进行协同文档的设计,早期使用了其他开发好的组件,但是随着客户需求的变更,现在需要自主设计同步类型,故开始阅读研究websocket的使用,所幸Yjs提供了websocket类型的Provider,能够节省不少开发的时间。

y-websocket

Websocket Provider实现了一个传统的客户端-服务器模型。这意味着它遵循标准的网络通信模式,其中客户端通过Websocket连接到一个单一的服务器端点,服务器负责在客户端之间分发文档更新和感知信息(如用户的在线状态、光标位置等)。

更进一步的,用户可以通过在服务器上配置Provider,这将能够持久化Doc对象更新或扩展基础设施。例如,你可以将文档更新保存到数据库中,或者通过添加更多服务器来处理更多的客户端连接。

如果使用Websocket Provider作为中心源来处理认证和授权,则服务器可以管理用户身份验证和权限控制,并且Websocket通信可以发送头部信息和cookies,因此可以使用现有的认证机制与服务器交互。这允许集成现有的用户认证系统。

y-websocket主要包含两个特点:

  • 支持跨标签页通信。当你在同一浏览器的不同标签页中打开相同的文档时,文档的更改可以通过跨标签页通信来交换。当你在同一浏览器的不同标签页中打开相同的文档时,文档的更改会通过广播频道(Broadcast Channel)进行交换,如果广播频道不可用,则回退到localStorage
  • 支持交换意识信息,例如光标位置。这意味着当多个用户编辑同一文档时,你可以看到其他用户的光标位置和其他相关信息。

快速开始

npm i y-websocket
import * as Y from 'yjs'
import { WebsocketProvider } from 'y-websocket'

const doc = new Y.Doc()
const wsProvider = new WebsocketProvider('ws://localhost:1234', 'my-roomname', doc)

wsProvider.on('status', event => {
  console.log(event.status) // logs "connected" or "disconnected"
})

这段代码设置了一个Yjs文档实例,并通过WebsocketProvider连接到一个本地服务器,用于在指定的房间名称下同步文档状态。同时,它还设置了一个事件监听器来监听连接状态的变化,并在控制台输出连接或断开连接的信息。

特别的,在NodeJS中

WebSocket 提供程序需要 WebSocket 对象来创建与服务器的连接。在NodeJS环境下,可以使用 ws 包在 NodeJS 中填充 WebSocket 支持。

npm i ws
const ws = require('ws')

const wsProvider = new WebsocketProvider(
  'ws://localhost:1234', 'my-roomname',
  doc,
  { WebSocketPolyfill: ws }
)

WebSocketPolyfilly-websocket 或其他类似库中的主要作用是确保WebSocket连接的兼容性和可靠性,即使在NodeJS环境中可能没有原生的WebSocket支持。

除了基本的WebSocket功能,WebSocketPolyfill 可能会提供额外的功能,比如自动重连、心跳机制(ping/pong) 来保持连接活跃、更好的错误处理等。并且WebSocketPolyfill 允许用户修改或扩展WebSocket的行为,以适应特定的需求。

Websocket的启动

在 y-websocket 的 Github 仓库中,给出了直接通过 y-websocket 启动一个Websocket服务器的基本命令。

修改于 2025/04/06
我的电脑由于不可抗力进行了系统重装,重装后发现无法直接使用y-websocket快速启动服务器
根据github的内容显示,现需要安装@y/websocket-server后才可启动

npm install @y/websocket-server

Linux

HOST=localhost PORT=1234 npx y-websocket

Windows

$env:HOST="localhost";$env:PORT="1234"; npx y-websocket

以上是最简单的websocket服务器启动命令,将会在本地1234端口启动默认的Websocket服务器。

如果需要自定义相关内容,则需要使用具体的配置文件启动传统Websocket服务器。

创建一个名为server.js的文件,并添加以下代码

const WebSocket = require('ws');

// 创建WebSocket服务器实例,监听在8080端口
const wss = new WebSocket.Server({ port: 8080 });

// 监听连接事件
wss.on('connection', function connection(ws) {
  console.log('Client connected');

  // 监听客户端发送的消息
  ws.on('message', function incoming(message) {
    console.log('received: %s', message);

    // 将接收到的消息发送回客户端
    ws.send(`Hello, you sent -> ${message}`);
  });

  // 监听连接关闭事件
  ws.on('close', () => {
    console.log('Client disconnected');
  });
});

console.log('WebSocket server is running on ws://localhost:8080');

在命令行中运行服务器

node server.js
持续化Websocket服务器
PORT=1234 YPERSISTENCE=./dbDir node ./node_modules/y-websocket/bin/server.js

该命令将文档更新保留在 LevelDB 数据库中。有关更多信息,请参阅 LevelDB 持久性。

这部分内容我后续会研究使用Redis进行持续化,故这里先不展开

带有HTTP返回的Websocket服务器

为了在文档更新时向 HTTP 服务器 (POST) 发送去抖动回调,可以采用以下环境变量:

CALLBACK_URL:回调服务器 URL

CALLBACK_DEBOUNCE_WAIT:回调之间的去抖动时间(以毫秒为单位)。默认为 2000 毫秒

CALLBACK_DEBOUNCE_MAXWAIT:回调前等待的最长时间。默认为 10 秒

CALLBACK_TIMEOUT:HTTP 调用的超时时间。默认为 5 秒

CALLBACK_OBJECTS:用于获取数据的共享对象的 JSON('{"SHARED_OBJECT_NAME":"SHARED_OBJECT_TYPE}')

CALLBACK_URL=http://localhost:3000/ CALLBACK_OBJECTS='{"prosemirror":"XmlFragment"}' npm start

这会在收到更新(默认 DEBOUNCE_WAIT)后 2 秒向 localhost:3000 发送一个去抖动回调,更新内容为主体中名为“prosemirror”XmlFragment 的数据。

另外

就我查到的资料来说,通常是说WebsocketProvider 不会自动配置 WebSocket 服务器。WebsocketProvider 仅仅是一个客户端库,它用于连接到一个已经运行并配置好的 WebSocket 服务器。

但是在我的实践中,我发现我项目迁移到新电脑后,直接在vue页面中使用y-websocket连接到原端口上不存在的 websocket 服务器时,该项目依旧能够实现协同通信的效果,我也找不到我有写过什么自动启动的脚本,令人感慨,不知道是不是使用Nuxt进行部署会有什么特殊之处。

API

import { WebsocketProvider } from 'y-websocket'
  • wsProvider = new WebsocketProvider(serverUrl: string, room: string, ydoc: Y.Doc [, wsOpts: WsOpts])
    创建一个新的 websocket-provider 实例。只要此提供程序或连接的 ydoc 未被销毁,更改将通过连接的服务器同步到其他客户端。或者,您可以指定一个配置对象,以可以覆盖以下 wsOpts 提供的默认值。

    wsOpts = {
        // 如果您想使用 wsProvider.connect() 手动连接,请将其设置为 `false`
        connect: true,
        // 指定将进行 url 编码并附加到 `serverUrl` 的查询字符串
        // 即 params = { auth: "bearer" } 将转换为 "?auth=bearer"
        params: {}, // Object<string,string>
        // 您可以 polyill Websocket 对象 (https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)。
        // 例如在 nodejs 中,您可以指定 WebsocketPolyfill = require('ws')
        WebsocketPolyfill: Websocket,
        // 指定现有的 Awareness 实例 - 请参阅 https://github.com/yjs/y-protocols
        awareness: new awarenessProtocol.Awareness(ydoc)
    }
    
  • wsProvider.wsconnected: boolean
    如果此实例当前已连接到服务器,则为 True。

  • wsProvider.wsconnecting: boolean
    如果此实例当前正在连接到服务器,则为 True。

  • wsProvider.shouldConnect: boolean
    如果为False,客户端将不会尝试重新连接。

  • wsProvider.bcconnected: boolean
    如果此实例当前正在通过 BroadcastChannel 与其他浏览器窗口进行通信,则为 True。

  • wsProvider.synced: boolean
    如果此实例当前已连接且与服务器同步,则为 True。

  • wsProvider.disconnect()
    断开与服务器的连接并且不要尝试重新连接。

  • wsProvider.connect()
    与 websocket 服务器建立 websocket 连接。如果您最近断开连接或设置了wsOpts.connect = false,请调用此方法。

  • wsProvider.destroy()
    销毁此wsProvider实例。断开与服务器的连接并删除所有事件处理程序。

  • wsProvider.on('sync', function(isSynced: boolean))
    添加一个事件监听器,用于监听客户端从服务器接收内容时触发的sync事件。

### ESP32与Node-RED集成开发指南 #### 一、环境准备 为了使ESP32能够通过Node-RED进行编程控制,需先搭建好必要的软件环境。这包括但不限于安装Node.js及其配套工具npm,在电脑上部署Node-RED平台,并确保已正确设置网络连接以便于后续操作[^1]。 对于硬件方面,则要准备好支持Wi-Fi功能的ESP32模块并将其接入互联网;另外还需考虑是否需要用到额外的外围组件如继电器、LED灯或其他类型的传感器来扩展项目应用范围[^2]。 #### 二、Node-RED节点创建 进入Node-RED编辑界面后,可以通过拖拽方式快速构建工作流。针对ESP32的应用场景,通常会涉及到MQTT协议用于消息传递。因此建议引入专门处理该类通信任务的功能块——`mqtt-broker`和`mqtt-out`/`mqtt-in`节点组合。 当目标是实现远程灯光控制系统时,可以利用HTTP请求或WebSocket等其他形式的数据交换机制作为替代方案之一。此时应相应调整所选用的具体节点类型以匹配特定需求[^3]。 ```json [ { "id": "mqttBroker", "type": "mqtt-broker", "broker": "<your-mqtt-server>", "port": "1883" }, { "id": "inputSwitch", "type": "ui_switch", "z": "", "name": "Light Switch", "label": "Toggle Light", "group": "default", "order": 0, "width": 0, "height": 0, "passthru": true, "decouple": false, "topic": "/light/status", "style": "" }, { "id": "publishToEsp", "type": "mqtt out", "z": "", "name": "", "topic": "/light/set", "qos": "0", "retain": "false", "respTopic": "", "contentType": "", "userProps": "", "correl": "none", "brok": "mqttBroker", "x": 790, "y": 460, "wires": [] } ] ``` 上述JSON片段展示了如何定义一个简单的MQTT代理服务以及UI上的开关控件,后者用来发送指令给ESP32改变其状态。注意替换其中占位符(如<your-mqtt-server>)为实际使用的服务器地址信息。 #### 三、固件烧录至ESP32 完成云端逻辑设计之后,下一步就是编写适用于ESP32本地运行的程序代码。这里推荐使用Arduino IDE或者PlatformIO这类IDE来进行开发调试。重点在于初始化WiFi连接参数、订阅指定主题的消息队列,并根据接收到的内容执行相应的动作响应。 考虑到不同开发者可能偏好各异的技术栈,如果更倾向于Python风格语法的话也可以尝试MicroPython框架下的解决方案。无论哪种途径都离不开对官方文档的学习掌握过程。 ```cpp #include <WiFi.h> #include <PubSubClient.h> // WiFi credentials. const char* ssid = "YOUR_SSID"; const char* password = "YOUR_PASSWORD"; // MQTT Broker settings. const char* mqtt_server = "BROKER_IP_ADDRESS"; WiFiClient espClient; PubSubClient client(espClient); void setup_wifi() { delay(10); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } randomSeed(micros()); Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); if (client.connect("ESP32 Client")) { Serial.println("connected"); // Once connected, publish an announcement... client.publish("/outTopic", "hello world"); // ... and resubscribe client.subscribe("/inTopic"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); delay(5000); } } } ``` 此C++样例提供了基本的WiFi联网及MQTT客户机实例化流程,便于理解整个系统的运作原理。当然具体细节还需要依据个人项目的实际情况作出适当修改完善。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值