.net8創建tcp服務接收數據通過websocket廣播

注冊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 容器的高可用版部署是兩種常見的方式。本文將深入對比這兩…