.net8創(chuàng)建tcp服務(wù)接收數(shù)據(jù)通過(guò)websocket廣播的實(shí)現(xiàn)代碼
注冊(cè)TCP服務(wù)器 注冊(cè)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); // 注冊(cè)TCP服務(wù) builder.Services.AddSingleton<TcpServer>(); builder.Services.AddHostedService(sp => sp.GetRequiredService<TcpServer>()); // 注冊(cè)WebSocket中間件 builder.Services.AddSingleton<WebSocketManager>(); builder.WebHost.UseUrls("http://*:5000");//指定websocket端口號(hào) 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服務(wù)實(shí)現(xiàn)
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) { // 服務(wù)停止時(shí)正常退出 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 來(lái)自?xún)x器."); Console.WriteLine($"收到 {bytesRead} bytes 來(lái)自?xún)x器."); // 解析數(shù)據(jù)并生成應(yīng)答 var result = await ParseData(receivedData/*, out byte[] response*/); if (result.success) { Log.Information(result.message); Console.WriteLine(result.message); //響應(yīng)發(fā)送 if (result.response.Length > 0) await stream.WriteAsync(result.response, 0, result.response.Length); Log.Information($"響應(yīng)發(fā)送."); Console.WriteLine("響應(yīng)發(fā)送."); } 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; // 血壓計(jì)上傳數(shù)據(jù)開(kāi)始碼 private static readonly byte[] ZL_Header = { 0x55, 0xAA }; // 解析數(shù)據(jù)包 private async Task<(bool success, byte[] response, string message)> ParseData(byte[] buffer/*, out byte[] response*/) { //response = null; // 檢查前導(dǎo)碼 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], "前導(dǎo)碼錯(cuò)誤"); } //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) { // 計(jì)算校驗(yàn)和(前12字節(jié)的累加和) ushort calculatedChecksum = 0; for (int i = 0; i < 12; i++) calculatedChecksum += buffer[i]; // 讀取數(shù)據(jù)包中的校驗(yàn)和(大端序) ushort packetChecksum = (ushort)((buffer[12] << 8) | buffer[13]); // 校驗(yàn)和驗(yàn)證 if (calculatedChecksum != packetChecksum) throw new ArgumentException($"Checksum mismatch: calculated 0x{calculatedChecksum:X4}, received 0x{packetChecksum:X4}"); // 解析數(shù)據(jù) 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] }; // 處理運(yùn)行模式/權(quán)值 byte modeWeight = buffer[6]; if (modeWeight >= 10 && modeWeight <= 17) result.OperationMode = GetModeName(modeWeight); else result.DataWeight = modeWeight; // 解析報(bào)警標(biāo)志 result.AlarmFlags = ParseAlarmFlags(buffer[7]); // 解析數(shù)據(jù)值(2字節(jié)) ushort rawValue = (ushort)((buffer[10] << 8) | buffer[11]); // 特殊處理有符號(hào)數(shù)據(jù) if (new[] { 0x0A, 0x0B, 0x0E }.Contains(result.DataIdentifier)) rawValue = (ushort)(short)rawValue; // 保持二進(jìn)制表示,后續(xù)轉(zhuǎn)換為double // 應(yīng)用權(quán)值處理 result.DataValue = ApplyDataWeight(rawValue, result.DataWeight, result.DataIdentifier); // 設(shè)置數(shù)據(jù)名稱(chēng)和單位 (result.DataName, result.Unit) = GetDataInfo(result.DataIdentifier); //return result; Console.WriteLine("Parsing successful!"); Console.WriteLine($"設(shè)備類(lèi)型: 0x{result.DeviceType:X2}");//Device Type Console.WriteLine($"設(shè)備id: 0x{result.DeviceId}");//十進(jìn)制 Device ID 16進(jìn)制:0x{result.DeviceId:X6} Console.WriteLine($"運(yùn)行模式: {result.OperationMode ?? "N/A"}");//Operation Mode Console.WriteLine($"數(shù)據(jù)權(quán)值: {result.DataWeight}");//Data Weight Console.WriteLine($"報(bào)警標(biāo)識(shí): [漏血BloodLeak: {result.AlarmFlags.BloodLeak}, " +//Alarms $"液位LiquidLevel: {result.AlarmFlags.LiquidLevel}, " + $"氣泡Bubble: {result.AlarmFlags.Bubble}, " + $"動(dòng)脈壓ArterialPressure: {result.AlarmFlags.ArterialPressure}, " + $"跨膜壓TransmembranePressure: {result.AlarmFlags.TransmembranePressure}, " + $"靜脈壓VenousPressure: {result.AlarmFlags.VenousPressure}, " + $"溫度Temperature: {result.AlarmFlags.Temperature}, " + $"電導(dǎo)Conductivity: {result.AlarmFlags.Conductivity}]"); Console.WriteLine($"其他報(bào)警: {result.HasOtherAlarm}");//Other Alarms Console.WriteLine($"數(shù)據(jù)標(biāo)識(shí): 0x{result.DataIdentifier:X2} ({result.DataName})");//數(shù)據(jù)標(biāo)識(shí) Console.WriteLine($"數(shù)據(jù)值: {result.DataValue} {result.Unit}");//數(shù)據(jù)值 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) { // 特殊處理電導(dǎo)值(數(shù)據(jù)標(biāo)識(shí)0x09) if (dataId == 0x09 && weight > 4) return rawValue; // 按整數(shù)顯示 return weight switch { 0 or 15 => rawValue, // 整數(shù) > 0 and <= 4 => rawValue / Math.Pow(10, weight), // 小數(shù)處理 _ => rawValue // 默認(rèn)按整數(shù)處理 }; } private (string name, string unit) GetDataInfo(byte dataId) { var dataMap = new Dictionary<byte, (string, string)> { { 0x01, ("dehydration", "L") },//脫水1 { 0x02, ("currentDehydration", "L") },//當(dāng)前的脫水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") },//透析液電導(dǎo)9 { 0x0A, ("venousPressure", "") },//靜脈壓力A { 0x0B, ("transmembranePressure", "") },//跨膜壓力B { 0x0C, ("dialyzedTime", "min") },//已透析時(shí)間C { 0x0D, ("remainingTime", "min") },//剩余透析時(shí)間D { 0x0E, ("arterialPressure", "") },//動(dòng)脈壓E { 0x0F, ("sphygmomanometerHigh", "") },//血壓計(jì)測(cè)量 高壓F { 0x10, ("sphygmomanometerLow", "") },//血壓計(jì)測(cè)量 低壓10 { 0x11, ("heartRate", "bpm") }//心率11 }; return dataMap.TryGetValue(dataId, out var info) ? info : ("Unknown", ""); } }
WebSocket服務(wù)
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 _); } } }
測(cè)試數(shù)據(jù)發(fā)送接收
# 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 測(cè)試工具:
瀏覽器開(kāi)發(fā)者工具
https://websocketking.com/
到此這篇關(guān)于.net8創(chuàng)建tcp服務(wù)接收數(shù)據(jù)通過(guò)websocket廣播的文章就介紹到這了,更多相關(guān).net8 tcp服務(wù)接收數(shù)據(jù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
asp. net下使用foreach簡(jiǎn)化文本文件的訪(fǎng)問(wèn)。
asp. net下使用foreach簡(jiǎn)化文本文件的訪(fǎng)問(wèn)。...2007-04-04C#(.NET)數(shù)據(jù)訪(fǎng)問(wèn)連接、查詢(xún)、插入等操作的封裝類(lèi)
一個(gè)C#(.NET)數(shù)據(jù)訪(fǎng)問(wèn)連接、查詢(xún)、插入等操作的封裝類(lèi)2008-05-05.NET?Core使用Redis實(shí)現(xiàn)創(chuàng)建分布式鎖
分布式鎖一般用于確保在分布式系統(tǒng)中,同一時(shí)間只有一個(gè)進(jìn)程可以執(zhí)行某段代碼,本文將通過(guò).NET?Core使用Redis實(shí)現(xiàn)創(chuàng)建分布式鎖,感興趣的可以了解下2025-01-01Entity?Framework代碼優(yōu)先Code?First入門(mén)
這篇文章介紹了Entity?Framework的代碼優(yōu)先模式Code?First,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06asp.net(c#)ref,out ,params的區(qū)別
C#中有三個(gè)關(guān)鍵字-ref,out ,params,雖然本人不喜歡這三個(gè)關(guān)鍵字,因?yàn)樗鼈円伤破茐拿嫦驅(qū)ο筇匦?。但是既然m$把融入在c#體系中,那么我們就來(lái)認(rèn)識(shí)一下參數(shù)修飾符ref,out ,params吧,還有它們的區(qū)別。2009-12-12asp.net Cookie跨域、虛擬目錄等設(shè)置方法
Cookie跨域、虛擬目錄等設(shè)置方法,需要的朋友可以參考下。2009-11-11ASP.NET實(shí)現(xiàn)的簡(jiǎn)單易用文件上傳類(lèi)
這篇文章主要介紹了ASP.NET實(shí)現(xiàn)的簡(jiǎn)單易用文件上傳類(lèi),本文給出實(shí)現(xiàn)代碼和使用方法示例,需要的朋友可以參考下2015-06-06