找回密码
 立即注册
首页 业界区 业界 MAUI 嵌入式 Web 架构实战(七) 构建设备实时通信与控 ...

MAUI 嵌入式 Web 架构实战(七) 构建设备实时通信与控制系统

瞿佳悦 昨天 23:45
MAUI 嵌入式 Web 架构实战(七)

PicoServer + WebSocket

构建设备实时通信与控制系统

源码地址
https://github.com/densen2014/MauiPicoAdmin
一、为什么需要 WebSocket

在前面的文章中,我们已经实现了完整架构:
  1. Web Admin UI
  2.       ↓
  3. PicoServer REST API
  4.       ↓
  5. MAUI Service
  6.       ↓
  7. SQLite / Device
复制代码
Web API 适合:
  1. CRUD
  2. 请求响应
复制代码
但对于 实时系统,REST API 有明显局限:
例如:
场景REST API 问题设备状态变化需要不断轮询实时日志延迟高设备控制交互慢例如浏览器:
  1. 每秒请求一次
  2. /api/device/status
复制代码
这叫:
Polling(轮询)
问题:
  1. 服务器压力大
  2. 延迟高
  3. 体验差
复制代码
解决方案是:
WebSocket
二、什么是 WebSocket

WebSocket 是一种 长连接通信协议
通信模式:
  1. 浏览器
  2.    ⇅
  3. WebSocket
  4.    ⇅
  5. Server
复制代码
特点:
特性说明双向通信Client / Server 都能发送长连接不需要重复建立连接实时性毫秒级因此非常适合:
  1. 设备控制
  2. 实时日志
  3. 消息推送
  4. IoT系统
复制代码
三、系统架构升级

加入 WebSocket 后架构变成:
  1.            Web Admin
  2.              │
  3.       ┌──────┴───────┐
  4.       │              │
  5. REST API        WebSocket
  6.       │              │
  7.       ▼              ▼
  8.          PicoServer
  9.               │
  10.               ▼
  11.             Service
  12.               │
  13.               ▼
  14.          Device / DB
复制代码
REST API:
  1. CRUD
复制代码
WebSocket:
  1. 实时通信
  2. 设备控制
复制代码
四、代码实现 WebSocket 服务器

在 PicoServer 中增加:
  1. WebSocketManager
复制代码
创建:
  1. Services/WebSocketManager.cs
复制代码
示例实现:
  1. using PicoServer;
  2. public class WebSocketManager
  3. {
  4.     private WebAPIServer? api;
  5.     public void RegisterWebSocket(WebAPIServer api)
  6.     {
  7.         this.api = api;
  8.         api.enableWebSocket = true;
  9.         api.WsOnConnectionChanged = WsConnectChanged;
  10.         api.WsOnMessage = OnMessageReceived;
  11.     }
  12.     public async Task OnMessageReceived(string clientId, string message, Func<string, Task> reply)
  13.     {
  14.         await reply("收到!");
  15.         var clients = api!.WsGetOnlineClients();
  16.         foreach (var client in clients)
  17.         {
  18.             await api.WsSendToClientAsync(client, $"{clientId}说:{message}");
  19.         }
  20.     }
  21.     //相关方法
  22.     //api.enableWebSocket = true; //启用WebSocket支持
  23.     //api.WsOnConnectionChanged; // 事件:WebSocket客户端连接状态发生变化
  24.     //api.WsOnMessage; //事件:收到WebSocket客户端发送来的消息
  25.     //api.WsBroadcastAsync(); //对所有在线客户端广播消息
  26.     //api.WsGetOnlineClients; //获取在线客户端列表
  27.     //api.WsSendToClientAsync(client, message); //给指定客户端发送消息
  28.     //api.WsEnableHeartbeat = true; //启用 WebSocket 服务端心跳检测,默认false
  29.     //api.WsHeartbeatTimeout = 60; //设置 WebSocket 心跳时间,默认30秒
  30.     //api.WsMaxConnections = 200; //设置 WebSocket 最大连接数,默认100
  31.     //api.WsPingString = "hi"; //设置 WebSocket 的ping消息,默认"pong",不区分大小写
  32.     public async Task WsConnectChanged(string clientId, bool connected)
  33.     {
  34.         await api!.WsBroadcastAsync($"{clientId} {connected}");
  35.     }
  36. }
复制代码
这个组件实现了:
  1. 连接管理
  2. 消息接收
  3. 消息广播
复制代码
五、注册 WebSocket 路由

在 ServerHost 中注册:
  1. ws.RegisterWebSocket(api);
复制代码
现在浏览器可以连接:
  1. ws://localhost:8090/ws
复制代码
六、前端连接 WebSocket

在 Web Admin 中:
  1. const ws = new WebSocket("ws://127.0.0.1:8090/ws");
  2. ws.onopen = () => {
  3.   console.log("WebSocket Connected");
  4. };
  5. ws.onmessage = (event) => {
  6. console.log("Message:", event.data);
  7. };
  8. ws.onclose = () => {
  9. console.log("Disconnected");
  10. };
  11. function send() {
  12. ws.send("hello device");
  13.         }
复制代码
发送消息:
  1. ws.send("hello device");
复制代码
七、设备控制协议设计

实际系统中需要定义 通信协议
推荐使用 JSON。
例如:
设备控制:
  1. {
  2.   "type": "device_control",
  3.   "device": "printer",
  4.   "cmd": "start"
  5. }
复制代码
设备状态:
  1. {
  2.   "type": "device_status",
  3.   "device": "printer",
  4.   "status": "running"
  5. }
复制代码
日志消息:
  1. {
  2.   "type": "log",
  3.   "message": "device started"
  4. }
复制代码
八、Server 处理设备命令

解析 WebSocket 消息:
  1. var cmd = JsonSerializer.Deserialize<WsCommand>(msg);
  2. switch(cmd.Type)
  3. {
  4.     case "device_control":
  5.         DeviceService.Execute(cmd.Device, cmd.Cmd);
  6.         break;
  7. }
复制代码
示例:
  1. DeviceService.Execute("printer","start");
复制代码
九、实时推送设备状态

当设备状态变化时:
  1. await ws.Broadcast(JsonSerializer.Serialize(new
  2. {
  3.     type = "device_status",
  4.     device = "printer",
  5.     status = "running"
  6. }));
复制代码
前端立即收到:
  1. ws.onmessage = e => {
  2.     let msg = JSON.parse(e.data);
  3.     if(msg.type === "device_status")
  4.     {
  5.         updateUI(msg);
  6.     }
  7. }
复制代码
实现:
  1. 设备 → Server → Web Admin
复制代码
实时更新。
完整前端代码
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="utf-8">
  5.     <title>WebSocket</title>
  6.     <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
  7.           rel="stylesheet">
  8. </head>
  9. <body >
  10.     <h1 >WebSocket</h1>
  11.    
  12.         <button onclick="send()" >发送消息</button>
  13.         <button onclick="start()" >控制设备(start)</button>
  14.         <button onclick="stop()" >控制设备(stop)</button>
  15.         <button onclick="startSSE()" >HTTP消息推送(SSE Demo)</button>
  16.    
  17.     <p id="result"></p>
  18.    
  19. </body>
  20. </html>
复制代码
后端代码
  1.     public async Task OnMessageReceived(string clientId, string message, Func<string, Task> reply)
  2.     {
  3.         await reply("收到!");
  4.         var clients = api!.WsGetOnlineClients();
  5.         foreach (var client in clients)
  6.         {
  7.             await api.WsSendToClientAsync(client, $"{clientId}说:{message}");
  8.         }
  9.         try
  10.         {
  11.             var cmd = JsonSerializer.Deserialize<WsCommand>(message, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
  12.             if (cmd != null)
  13.             {
  14.                 switch (cmd.Type)
  15.                 {
  16.                     case "device_control":
  17.                         await Task.Delay(200);//模拟执行控制命令
  18.                         //DeviceService.Execute(cmd.Device, cmd.Cmd);
  19.                         await api.WsBroadcastAsync(JsonSerializer.Serialize(new
  20.                         {
  21.                             type = "device_status",
  22.                             device = "printer",
  23.                             status = cmd.Cmd == "start" ? "running" : "stop"
  24.                         }));
  25.                         break;
  26.                 }
  27.             }
  28.         }
  29.         catch (JsonException)
  30.         {
  31.             // 处理 JSON 解析错误
  32.         }
  33.     }
  34.     //SSE(Server - Sent Events)推送, 请注册路由 api.AddRoute("/iot/notify", HttpHelper.Notify, "GET");
  35.     public static async Task Notify(HttpListenerRequest request, HttpListenerResponse response)
  36.     {
  37.         response.ContentType = "text/event-stream";
  38.         response.Headers.Add("Cache-Control", "no-cache");
  39.         response.SendChunked = true;
  40.         try
  41.         {
  42.             for (int i = 0; i < 5; i++)
  43.             {
  44.                 string msg = $"data: 消息推送 {i} 时间: {DateTime.Now}\n\n";
  45.                 byte[] buffer = System.Text.Encoding.UTF8.GetBytes(msg);
  46.                 await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
  47.                 await response.OutputStream.FlushAsync();
  48.                 await Task.Delay(1000);
  49.             }
  50.         }
  51.         finally
  52.         {
  53.             //在使用 HttpListenerResponse 进行 SSE(Server - Sent Events)推送时,response.Close(); 并不是必须的,但推荐在推送结束后调用它,以确保资源释放和连接正确关闭。
  54.             // 示例这里是推送结束后调用 response.Close();,确保响应流关闭
  55.             // 如果是无限推送(如实时设备报警),不要关闭响应,直到客户端断开。
  56.             response.Close();
  57.         }
  58.     }
复制代码
运行截图
1.png

十、实时日志系统

例如设备日志:
  1. await ws.Broadcast(JsonSerializer.Serialize(new
  2. {
  3.     type="log",
  4.     message="print job started"
  5. }));
复制代码
前端:
  1. if(msg.type==="log"){
  2.    logPanel.append(msg.message)
  3. }
复制代码
效果:
  1. 实时日志窗口
复制代码
十一、完整实时架构

最终系统变成:
  1.                  Web Admin UI
  2.                 /            \
  3.           REST API        WebSocket
  4.               │               │
  5.               ▼               ▼
  6.              PicoServer Core
  7.                     │
  8.                     ▼
  9.                  Services
  10.                     │
  11.           ┌─────────┴─────────┐
  12.           ▼                   ▼
  13.        SQLite              Device
复制代码
系统能力升级为:
  1. 后台管理
  2. +
  3. 实时通信
  4. +
  5. 设备控制
  6. +
  7. 数据存储
复制代码
十二、本篇总结

本篇为系统新增:
核心能力:
  1. WebSocket 实时通信
  2. 设备控制协议实时日志推送状态同步
复制代码
系统能力升级为:
  1. Web Admin
  2. +
  3. REST API
  4. +
  5. WebSocket
  6. +
  7. 设备控制
复制代码
已经可以用于:
  1. IoT 系统
  2. 设备管理平台
  3. 本地控制软件
  4. 工业工具系统
复制代码
下一篇预告

下一篇将进入 架构升级的重要一步
MAUI 嵌入式 Web 架构实战(八)

插件化架构与模块系统

我们将实现:
  1. 插件加载
  2. 模块扩展
  3. 动态 API
  4. 模块管理
复制代码
最终把系统升级为:
真正可扩展的本地 Web 平台

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册