注册TCP服务器 注册WebSocket中间件

using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.AspNetCore.WebSockets;var builder = WebApplication.CreateBuilder(args);// 注册TCP服务
builder.Services.AddSingleton<TcpServer>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<TcpServer>());// 注册WebSocket中间件
builder.Services.AddSingleton<WebSocketManager>();builder.WebHost.UseUrls("http://*:5000");//指定websocket端口号var app = builder.Build();// WebSocket中间件
app.UseWebSockets();
app.Use(async (context, next) =>
{if (context.WebSockets.IsWebSocketRequest){var webSocketManager = context.RequestServices.GetRequiredService<WebSocketManagement>();var webSocket = await context.WebSockets.AcceptWebSocketAsync();await webSocketManager.HandleWebSocketConnectionAsync(webSocket);}else{await next(context);}
});app.Run();

tcp服务实现

public class TcpServer : BackgroundService
{private readonly WebSocketManagement _webSocketManager;private const int Port = 8081;private const int PacketSize = 14;private const int CheckSumSize = 2;private TcpListener? _listener;public TcpServer(WebSocketManagement webSocketManager){_webSocketManager = webSocketManager;}protected override async Task ExecuteAsync(CancellationToken stoppingToken){_listener = new TcpListener(IPAddress.Any, Port);_listener.Start();Console.WriteLine($"TCP server started on port {Port}");try{while (!stoppingToken.IsCancellationRequested){try{var client = await _listener.AcceptTcpClientAsync(stoppingToken);_ = HandleClientAsync(client, stoppingToken);}catch (OperationCanceledException){// 服务停止时正常退出break;}}}finally{_listener.Stop();Console.WriteLine("TCP server stopped");}}private async Task HandleClientAsync(TcpClient client, CancellationToken ct){var clientId = Guid.NewGuid().ToString();Console.WriteLine($"Client connected: {clientId}");using (client){byte[] buffer = new byte[1024];var stream = client.GetStream();while (!ct.IsCancellationRequested){try{int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);if (bytesRead > 0){byte[] receivedData = new byte[bytesRead];Array.Copy(buffer, receivedData, bytesRead);Log.Information($"收到 {bytesRead} bytes 来自仪器.");Console.WriteLine($"收到 {bytesRead} bytes 来自仪器.");// 解析数据并生成应答var result = await ParseData(receivedData/*, out byte[] response*/);if (result.success){Log.Information(result.message);Console.WriteLine(result.message);//响应发送if (result.response.Length > 0) await stream.WriteAsync(result.response, 0, result.response.Length);Log.Information($"响应发送.");Console.WriteLine("响应发送.");}else{Log.Information($"Error: {result.message}");Console.WriteLine($"Error: {result.message}");}} }catch (IOException ex){Console.WriteLine($"Client {clientId} connection error: {ex.Message}");return;}catch (ObjectDisposedException){Console.WriteLine($"Client {clientId} connection closed");return;}catch (Exception ex){Console.WriteLine(ex.Message, $"Error processing client {clientId}");return;}}}} //血透private const int ZL_PacketLength = 14;public const byte ZL_START_CODE_UPLOAD = 0x55; // 血压计上传数据开始码private static readonly byte[] ZL_Header = { 0x55, 0xAA };// 解析数据包private async Task<(bool success, byte[] response, string message)> ParseData(byte[] buffer/*, out byte[] response*/){//response = null; // 检查前导码if (buffer.Length == ZL_PacketLength && buffer[0] == ZL_Header[0] && buffer[1] == ZL_Header[1]){//var result =await HemodialysisZLMonitor(buffer );return (result.success,new byte[0], result.message);}else{return (false, new byte[0], "前导码错误");}//return (result.success, result.message);} /// <summary>/// 仪器(/// </summary>/// <param name="buffer"></param>/// <param name="response"></param>/// <returns></returns>private async Task<(bool success , string message)> HemodialysisZLMonitor(byte[] buffer){ // 计算校验和(前12字节的累加和)ushort calculatedChecksum = 0;for (int i = 0; i < 12; i++)calculatedChecksum += buffer[i];// 读取数据包中的校验和(大端序)ushort packetChecksum = (ushort)((buffer[12] << 8) | buffer[13]);// 校验和验证if (calculatedChecksum != packetChecksum)throw new ArgumentException($"Checksum mismatch: calculated 0x{calculatedChecksum:X4}, received 0x{packetChecksum:X4}");// 解析数据var result = new ZL_ParsedData{DeviceType = buffer[2],DeviceId = (uint)((buffer[3] << 16) | (buffer[4] << 8) | buffer[5]),HasOtherAlarm = buffer[8] != 0,DataIdentifier = buffer[9]};// 处理运行模式/权值byte modeWeight = buffer[6];if (modeWeight >= 10 && modeWeight <= 17)result.OperationMode = GetModeName(modeWeight);elseresult.DataWeight = modeWeight;// 解析报警标志result.AlarmFlags = ParseAlarmFlags(buffer[7]);// 解析数据值(2字节)ushort rawValue = (ushort)((buffer[10] << 8) | buffer[11]);// 特殊处理有符号数据if (new[] { 0x0A, 0x0B, 0x0E }.Contains(result.DataIdentifier))rawValue = (ushort)(short)rawValue; // 保持二进制表示,后续转换为double// 应用权值处理result.DataValue = ApplyDataWeight(rawValue, result.DataWeight, result.DataIdentifier);// 设置数据名称和单位(result.DataName, result.Unit) = GetDataInfo(result.DataIdentifier);//return result;Console.WriteLine("Parsing successful!");Console.WriteLine($"设备类型: 0x{result.DeviceType:X2}");//Device TypeConsole.WriteLine($"设备id: 0x{result.DeviceId}");//十进制 Device ID   16进制:0x{result.DeviceId:X6}Console.WriteLine($"运行模式: {result.OperationMode ?? "N/A"}");//Operation ModeConsole.WriteLine($"数据权值: {result.DataWeight}");//Data WeightConsole.WriteLine($"报警标识: [漏血BloodLeak: {result.AlarmFlags.BloodLeak}, " +//Alarms$"液位LiquidLevel: {result.AlarmFlags.LiquidLevel}, " +$"气泡Bubble: {result.AlarmFlags.Bubble}, " +$"动脉压ArterialPressure: {result.AlarmFlags.ArterialPressure}, " +$"跨膜压TransmembranePressure: {result.AlarmFlags.TransmembranePressure}, " +$"静脉压VenousPressure: {result.AlarmFlags.VenousPressure}, " +$"温度Temperature: {result.AlarmFlags.Temperature}, " +$"电导Conductivity: {result.AlarmFlags.Conductivity}]");Console.WriteLine($"其他报警: {result.HasOtherAlarm}");//Other AlarmsConsole.WriteLine($"数据标识: 0x{result.DataIdentifier:X2} ({result.DataName})");//数据标识Console.WriteLine($"数据值: {result.DataValue} {result.Unit}");//数据值WebSocketSend webSocketSend = new WebSocketSend { Name= result.DataName,Value= result.DataValue };await _webSocketManager.BroadcastAsync(Convert.ToString(result.DeviceId), webSocketSend);return (true,"");}private ZL_AlarmFlags ParseAlarmFlags(byte flagByte){return new ZL_AlarmFlags{BloodLeak = (flagByte & 0x01) != 0,LiquidLevel = (flagByte & 0x02) != 0,Bubble = (flagByte & 0x04) != 0,ArterialPressure = (flagByte & 0x08) != 0,TransmembranePressure = (flagByte & 0x10) != 0,VenousPressure = (flagByte & 0x20) != 0,Temperature = (flagByte & 0x40) != 0,Conductivity = (flagByte & 0x80) != 0};}private string GetModeName(byte mode){return mode switch{10 => "Dialysis",12 => "LowSuper",13 => "SingleSuper",14 => "BloodReturn",15 => "Precharge",16 => "SelfTest",17 => "Disinfection",_ => "Unknown"};}private double ApplyDataWeight(ushort rawValue, int weight, byte dataId){// 特殊处理电导值(数据标识0x09)if (dataId == 0x09 && weight > 4)return rawValue; // 按整数显示return weight switch{0 or 15 => rawValue,          // 整数> 0 and <= 4 => rawValue / Math.Pow(10, weight), // 小数处理_ => rawValue                 // 默认按整数处理};}private (string name, string unit) GetDataInfo(byte dataId){var dataMap = new Dictionary<byte, (string, string)>{{ 0x01, ("dehydration", "L") },//脱水1{ 0x02, ("currentDehydration", "L") },//当前的脱水2{ 0x03, ("dehydrationSpeed", "L/h") },//脱水速度3{ 0x04, ("bloodPumpFlow", "ml/min") },//血泵流量4{ 0x05, ("auxiliaryPump", "") },//辅助泵5{ 0x06, ("syringe", "ml/h") },//注射器6{ 0x07, ("dialysateFlow", "") },//透析液流量7{ 0x08, ("dialysateTemperature", "°C") },//透析液温度8{ 0x09, ("dialysateConductivity", "mS/cm") },//透析液电导9{ 0x0A, ("venousPressure", "") },//静脉压力A{ 0x0B, ("transmembranePressure", "") },//跨膜压力B{ 0x0C, ("dialyzedTime", "min") },//已透析时间C{ 0x0D, ("remainingTime", "min") },//剩余透析时间D{ 0x0E, ("arterialPressure", "") },//动脉压E{ 0x0F, ("sphygmomanometerHigh", "") },//血压计测量 高压F{ 0x10, ("sphygmomanometerLow", "") },//血压计测量 低压10{ 0x11, ("heartRate", "bpm") }//心率11};return dataMap.TryGetValue(dataId, out var info)? info: ("Unknown", "");}}

WebSocket服务

public class WebSocketManagement
{private readonly ConcurrentDictionary<string, WebSocket> _sockets = new();public async Task HandleWebSocketConnectionAsync(WebSocket webSocket){var buffer = new byte[1024 * 4];var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);if (result.MessageType == WebSocketMessageType.Text){var deviceId = Encoding.UTF8.GetString(buffer, 0, result.Count);_sockets[deviceId.Trim()] = webSocket;Console.WriteLine("socket消息:" + deviceId);}while (webSocket.State == WebSocketState.Open){await Task.Delay(100);}}public async Task BroadcastAsync(string socketId, dynamic data){var json = JsonConvert.SerializeObject(data);var buffer = Encoding.UTF8.GetBytes(json);_sockets.TryGetValue(socketId, out WebSocket socket);if (socket!=null&&socket.State == WebSocketState.Open){await socket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);}else{_sockets.TryRemove(socketId, out _);}}
}

测试数据发送接收

# PowerShell
$data = [byte[]](0x55, 0xAA, 0x00, 0x26, 0x35, 0xB2, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00, 0x02, 0x1B)
$client = New-Object System.Net.Sockets.TcpClient('localhost', 8081)
$stream = $client.GetStream()
$stream.Write($data, 0, $data.Length)
$client.Close()

在这里插入图片描述

使用 WebSocket 测试工具:

浏览器开发者工具

https://websocketking.com/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/88964.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/88964.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/88964.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阅读服务使用示例(HarmonyOS Reader Kit)

阅读服务使用示例&#xff08;HarmonyOS Reader Kit&#xff09; Reader Kit到底能干啥&#xff1f; 第一次搞电子书阅读器&#xff0c;真以为就是“读txt显示出来”这么简单&#xff0c;结果各种格式、排版、翻页动效、目录跳转……全是坑。还好有Reader Kit&#xff0c;救了…

ASP.NET Core Web API 实现 JWT 身份验证

在ASP.NET Core WebApi中使用标识框架&#xff08;Identity)-CSDN博客 因为一般需要和标识框架一起使用,建议先查看标识框架用法 一.为什么需要JWT 我们的系统需要实现认证,即服务端需要知道登录进来的客户端的身份,管理员有管理员的权限,普通用户有普通用户的权限. 但服务…

优化Cereal宏 一行声明序列化函数

Cereal序列化库中宏递归展开的优化方案及技术解析 未优化&#xff1a;参考nlohmann json设计Cereal宏 一行声明序列化函数 宏实现 #include <cereal/cereal.hpp>// 强制二次展开 #define CEREAL_EXPAND( x ) x// 获取宏参数的数量&#xff0c;对应的CEREAL_PASTEn宏NAME…

14-C#的弹出的窗口输入与输出

C#的弹出的窗口输入与输出 1.文件名输入 string fileName Interaction.InputBox("输入保存的文件名", "保存");2.弹窗信息输出 MessageBox.Show("请选择轮询!", "Error", MessageBoxButtons.OK);catch (Exception ex){MessageBox.S…

多模态大语言模型arxiv论文略读(141)

Mini-InternVL: A Flexible-Transfer Pocket Multimodal Model with 5% Parameters and 90% Performance ➡️ 论文标题&#xff1a;Mini-InternVL: A Flexible-Transfer Pocket Multimodal Model with 5% Parameters and 90% Performance ➡️ 论文作者&#xff1a;Zhangwei …

VScode使用usb转网口远程开发rk3588

我使用的是鲁班猫的板&#xff0c;只有一个网口&#xff0c;需要接雷达&#xff0c;因此另外弄了一个usb转网口来连接电脑开发。 在使用vscode或MobaXterm连接板子时&#xff0c;使用主机名与用户名来连接&#xff1a; ssh catlubancat rk那边就直接插入usb转网口以及网线&a…

AUTOSAR图解==>AUTOSAR_AP_EXP_SOVD

AUTOSAR服务导向车辆诊断详解 面向现代化车辆架构的诊断方案 目录 1. 引言 1.1 ASAM SOVD简介1.2 SOVD产生的动机 2. SOVD参考架构 2.1 SOVD网关2.2 诊断管理器2.3 SOVD到UDS转换2.4 后端连接 3. SOVD用例 3.1 SOVD和UDS的共同用例3.2 SOVD特定用例 3.2.1 访问权限3.2.2 软件更…

第八讲:STL简介

1. 什么是STL STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复的 组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 2. STL的版本 a. 原始版本 Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本…

高弹性、高可靠!腾讯云 TDMQ RabbitMQ Serverless 版全新发布

导语 2025年6月起&#xff0c;腾讯云 TDMQ RabbitMQ 版正式推出 Serverless 版本&#xff0c;该版本基于自研的存算分离架构&#xff0c;兼容 AMQP 0-9-1 协议和开源 RabbitMQ 的各个组件与概念&#xff0c;且能够规避开源版本固有的不抗消息堆积、脑裂等稳定性缺陷&#xff0…

Linux 内存调优之 BPF 分析用户态小内存分配

写在前面 博文内容为 使用 BPF 工具跟踪 Linux 用户态小内存分配(brk,sbrk)理解不足小伙伴帮忙指正 😃,生活加油我看远山,远山悲悯 持续分享技术干货,感兴趣小伙伴可以关注下 _ brk 内存分配简单概述 一般来说,应用程序的数据存放于堆内存中,堆内存通过brk(2)系统调用进…

心理测评app心理测试系统框架设计

一、逻辑分析 用户管理逻辑 新用户注册&#xff1a;需要收集用户的基本信息&#xff0c;如用户名、密码、邮箱等&#xff0c;并且要对输入信息进行合法性校验&#xff0c;确保信息完整且符合格式要求。同时&#xff0c;为每个新用户生成唯一的标识符&#xff0c;方便后续数据管…

配置有nvlink的H20A800使用pytorch报错

背景 装有nvlink的h20机器上配置好驱动和cuda之后使用pytorch报错 A800机器同样 (pytorch2.4) rootxx-dev-H20:~# python Python 3.12.0 | packaged by Anaconda, Inc. | (main, Oct 2 2023, 17:29:18) [GCC 11.2.0] on linux Type “help”, “copyright”, “credits” or …

sql的语句执行过程

第一步&#xff1a;客户端把语句发给服务器端执行 当我们在客户端执行SQL语句时&#xff0c;客户端会把这条SQL语句发送给服务器端&#xff0c;让服务器端的进程来处理这语句。也就是说&#xff0c;Oracle 客户端是不会做任何的操作&#xff0c;他的主要任务就是把客户端产生的…

深度学习-分类

深度学习-分类方式 &#xff08;重点&#xff09;一、按数据类型与处理逻辑分类1. 序列数据&#xff08;时序/顺序相关&#xff09;2. 网格状数据&#xff08;空间相关&#xff09;3. 图结构数据&#xff08;非欧几里得结构&#xff09;4. 其他特殊类型数据 &#xff08;重点&a…

C语言---常见的字符函数和字符串函数介绍

目录 前言 1 字符分类函数 2 字符转换函数 3 strlen的使用和模拟实现 3.1 strlen的模拟实现 4 strcpy的使用和模拟实现 4.1 strcpy的模拟实现 5 strcat的使用和模拟实现 5.1 strcat的模拟实现 6 strcmp的使用和模拟实现 6.1 strcmp的模拟实现 7 strncpy函数的使用…

Minio入门+适配器模式(实战教程)

一、安装Minio 1.1 拉取镜像 docker pull minio/minio docker images 1.2创建挂载目录 1.2.1 创建数据目录 mkdir -p /docker-minio/data 1.2.2 创建配置文件目录 mkdir -p /docker-minio/config 1.2.3 设置权限 chmod -R 777 /docker-minio/data /docker-minio/config …

LLaMA-Factory 对 omnisql 进行 ppo dpo grpo nl2sql任务 实现难度 时间 全面对比

在LLaMA-Factory框架下&#xff0c;针对omnisql任务&#xff08;自然语言到SQL生成&#xff09;应用PPO、DPO、GRPO三种算法的实现难度、时间及全面对比如下&#xff1a; 一、实现难度对比 1. PPO&#xff08;近端策略优化&#xff09; 难度&#xff1a;★★☆☆☆&#xff…

Kingbase 数据库中的 sys_guid() 函数报错

解决 Kingbase 数据库中的 sys_guid() 函数报错问题 问题背景 Kingbase 数据库在迁移或使用过程中&#xff0c;可能会遇到 select sys_guid() 函数报错 , 提示函数不存在的情况&#xff0c;这通常是由于以下几种原因造成的&#xff1a; 函数未正确安装或未启用函数参数不符合…

零基础RT-thread第五节:电容按键(2)

上一章的电容按键完全使用的HAL库的代码&#xff0c;并没有使用线程。这里尝试使用线程来控制电容按键。 依旧是 F767 本来以为会很容易实现&#xff0c;没想到尝试了很久&#xff0c;电容按键一直没有反应。 static rt_uint32_t measure_charge_time(void) {// 步骤1: 放电 …

华为云Flexus+DeepSeek征文|单机部署 与 CCE 高可用部署下 Dify 性能实测

引言 在当今的 AI 应用开发领域&#xff0c;选择合适的部署方式对于应用的性能表现、资源利用和成本控制至关重要。华为云为开发者提供了多样化的部署选择&#xff0c;其中基于单机 Flexus 实例的基础版部署和基于 CCE 容器的高可用版部署是两种常见的方式。本文将深入对比这两…