设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间01 现在时间01 缓存数据 当你走完一段之后回头看,你会发现,那些真正能被记得的事真的是没有多少,真正无法忘记的人屈指可数,真正有趣的日子不过是那么一些,而真正需要害怕的也是寥寥无几。

当你走完一段之后回头看,你会发现,那些真正能被记得的事真的是没有多少,真正无法忘记的人屈指可数,真正有趣的日子不过是那么一些,而真正需要害怕的也是寥寥无几。

查看: 667|回复: 1

.net8创建tcp服务接收数据通过websocket广播的实现代码

[复制链接]

  离线 

TA的专栏

  • 打卡等级:无名新人
  • 打卡总天数:1
  • 打卡月天数:0
  • 打卡总奖励:7
  • 最近打卡:2025-11-30 12:58:11
等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
32
主题
28
精华
0
金钱
106
积分
62
注册时间
2023-10-3
最后登录
2025-11-30

发表于 2025-9-1 01:29:07 | 显示全部楼层 |阅读模式

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

  1. using System.Net;
  2. using System.Net.Sockets;
  3. using System.Text;
  4. using System.Text.Json;
  5. using Microsoft.AspNetCore.Builder;
  6. using Microsoft.AspNetCore.Http;
  7. using Microsoft.AspNetCore.SignalR.Client;
  8. using Microsoft.AspNetCore.WebSockets;
  9. var builder = WebApplication.CreateBuilder(args);
  10. // 注册TCP服务
  11. builder.Services.AddSingleton<TcpServer>();
  12. builder.Services.AddHostedService(sp => sp.GetRequiredService<TcpServer>());
  13. // 注册WebSocket中间件
  14. builder.Services.AddSingleton<WebSocketManager>();
  15. builder.WebHost.UseUrls("http://*:5000");//指定websocket端口号
  16. var app = builder.Build();
  17. // WebSocket中间件
  18. app.UseWebSockets();
  19. app.Use(async (context, next) =>
  20. {
  21. if (context.WebSockets.IsWebSocketRequest)
  22. {
  23. var webSocketManager = context.RequestServices.GetRequiredService<WebSocketManagement>();
  24. var webSocket = await context.WebSockets.AcceptWebSocketAsync();
  25. await webSocketManager.HandleWebSocketConnectionAsync(webSocket);
  26. }
  27. else
  28. {
  29. await next(context);
  30. }
  31. });
  32. app.Run();
复制代码

tcp服务实现

  1. public class TcpServer : BackgroundService
  2. {
  3. private readonly WebSocketManagement _webSocketManager;
  4. private const int Port = 8081;
  5. private const int PacketSize = 14;
  6. private const int CheckSumSize = 2;
  7. private TcpListener? _listener;
  8. public TcpServer(WebSocketManagement webSocketManager)
  9. {
  10. _webSocketManager = webSocketManager;
  11. }
  12. protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  13. {
  14. _listener = new TcpListener(IPAddress.Any, Port);
  15. _listener.Start();
  16. Console.WriteLine($"TCP server started on port {Port}");
  17. try
  18. {
  19. while (!stoppingToken.IsCancellationRequested)
  20. {
  21. try
  22. {
  23. var client = await _listener.AcceptTcpClientAsync(stoppingToken);
  24. _ = HandleClientAsync(client, stoppingToken);
  25. }
  26. catch (OperationCanceledException)
  27. {
  28. // 服务停止时正常退出
  29. break;
  30. }
  31. }
  32. }
  33. finally
  34. {
  35. _listener.Stop();
  36. Console.WriteLine("TCP server stopped");
  37. }
  38. }
  39. private async Task HandleClientAsync(TcpClient client, CancellationToken ct)
  40. {
  41. var clientId = Guid.NewGuid().ToString();
  42. Console.WriteLine($"Client connected: {clientId}");
  43. using (client)
  44. {
  45. byte[] buffer = new byte[1024];
  46. var stream = client.GetStream();
  47. while (!ct.IsCancellationRequested)
  48. {
  49. try
  50. {
  51. int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
  52. if (bytesRead > 0)
  53. {
  54. byte[] receivedData = new byte[bytesRead];
  55. Array.Copy(buffer, receivedData, bytesRead);
  56. Log.Information($"收到 {bytesRead} bytes 来自仪器.");
  57. Console.WriteLine($"收到 {bytesRead} bytes 来自仪器.");
  58. // 解析数据并生成应答
  59. var result = await ParseData(receivedData/*, out byte[] response*/);
  60. if (result.success)
  61. {
  62. Log.Information(result.message);
  63. Console.WriteLine(result.message);
  64. //响应发送
  65. if (result.response.Length > 0) await stream.WriteAsync(result.response, 0, result.response.Length);
  66. Log.Information($"响应发送.");
  67. Console.WriteLine("响应发送.");
  68. }
  69. else
  70. {
  71. Log.Information($"Error: {result.message}");
  72. Console.WriteLine($"Error: {result.message}");
  73. }
  74. }
  75. }
  76. catch (IOException ex)
  77. {
  78. Console.WriteLine($"Client {clientId} connection error: {ex.Message}");
  79. return;
  80. }
  81. catch (ObjectDisposedException)
  82. {
  83. Console.WriteLine($"Client {clientId} connection closed");
  84. return;
  85. }
  86. catch (Exception ex)
  87. {
  88. Console.WriteLine(ex.Message, $"Error processing client {clientId}");
  89. return;
  90. }
  91. }
  92. }
  93. }
  94. //血透
  95. private const int ZL_PacketLength = 14;
  96. public const byte ZL_START_CODE_UPLOAD = 0x55; // 血压计上传数据开始码
  97. private static readonly byte[] ZL_Header = { 0x55, 0xAA };
  98. // 解析数据包
  99. private async Task<(bool success, byte[] response, string message)> ParseData(byte[] buffer/*, out byte[] response*/)
  100. {
  101. //response = null;
  102. // 检查前导码
  103. if (buffer.Length == ZL_PacketLength && buffer[0] == ZL_Header[0] && buffer[1] == ZL_Header[1])
  104. {//
  105. var result =await HemodialysisZLMonitor(buffer );
  106. return (result.success,new byte[0], result.message);
  107. }
  108. else
  109. {
  110. return (false, new byte[0], "前导码错误");
  111. }
  112. //return (result.success, result.message);
  113. }
  114. /// <summary>
  115. /// 仪器(
  116. /// </summary>
  117. /// <param name="buffer"></param>
  118. /// <param name="response"></param>
  119. /// <returns></returns>
  120. private async Task<(bool success , string message)> HemodialysisZLMonitor(byte[] buffer)
  121. {
  122. // 计算校验和(前12字节的累加和)
  123. ushort calculatedChecksum = 0;
  124. for (int i = 0; i < 12; i++)
  125. calculatedChecksum += buffer[i];
  126. // 读取数据包中的校验和(大端序)
  127. ushort packetChecksum = (ushort)((buffer[12] << 8) | buffer[13]);
  128. // 校验和验证
  129. if (calculatedChecksum != packetChecksum)
  130. throw new ArgumentException($"Checksum mismatch: calculated 0x{calculatedChecksum:X4}, received 0x{packetChecksum:X4}");
  131. // 解析数据
  132. var result = new ZL_ParsedData
  133. {
  134. DeviceType = buffer[2],
  135. DeviceId = (uint)((buffer[3] << 16) | (buffer[4] << 8) | buffer[5]),
  136. HasOtherAlarm = buffer[8] != 0,
  137. DataIdentifier = buffer[9]
  138. };
  139. // 处理运行模式/权值
  140. byte modeWeight = buffer[6];
  141. if (modeWeight >= 10 && modeWeight <= 17)
  142. result.OperationMode = GetModeName(modeWeight);
  143. else
  144. result.DataWeight = modeWeight;
  145. // 解析报警标志
  146. result.AlarmFlags = ParseAlarmFlags(buffer[7]);
  147. // 解析数据值(2字节)
  148. ushort rawValue = (ushort)((buffer[10] << 8) | buffer[11]);
  149. // 特殊处理有符号数据
  150. if (new[] { 0x0A, 0x0B, 0x0E }.Contains(result.DataIdentifier))
  151. rawValue = (ushort)(short)rawValue; // 保持二进制表示,后续转换为double
  152. // 应用权值处理
  153. result.DataValue = ApplyDataWeight(rawValue, result.DataWeight, result.DataIdentifier);
  154. // 设置数据名称和单位
  155. (result.DataName, result.Unit) = GetDataInfo(result.DataIdentifier);
  156. //return result;
  157. Console.WriteLine("Parsing successful!");
  158. Console.WriteLine($"设备类型: 0x{result.DeviceType:X2}");//Device Type
  159. Console.WriteLine($"设备id: 0x{result.DeviceId}");//十进制 Device ID 16进制:0x{result.DeviceId:X6}
  160. Console.WriteLine($"运行模式: {result.OperationMode ?? "N/A"}");//Operation Mode
  161. Console.WriteLine($"数据权值: {result.DataWeight}");//Data Weight
  162. Console.WriteLine($"报警标识: [漏血BloodLeak: {result.AlarmFlags.BloodLeak}, " +//Alarms
  163. $"液位LiquidLevel: {result.AlarmFlags.LiquidLevel}, " +
  164. $"气泡Bubble: {result.AlarmFlags.Bubble}, " +
  165. $"动脉压ArterialPressure: {result.AlarmFlags.ArterialPressure}, " +
  166. $"跨膜压TransmembranePressure: {result.AlarmFlags.TransmembranePressure}, " +
  167. $"静脉压VenousPressure: {result.AlarmFlags.VenousPressure}, " +
  168. $"温度Temperature: {result.AlarmFlags.Temperature}, " +
  169. $"电导Conductivity: {result.AlarmFlags.Conductivity}]");
  170. Console.WriteLine($"其他报警: {result.HasOtherAlarm}");//Other Alarms
  171. Console.WriteLine($"数据标识: 0x{result.DataIdentifier:X2} ({result.DataName})");//数据标识
  172. Console.WriteLine($"数据值: {result.DataValue} {result.Unit}");//数据值
  173. WebSocketSend webSocketSend = new WebSocketSend { Name= result.DataName,Value= result.DataValue };
  174. await _webSocketManager.BroadcastAsync(Convert.ToString(result.DeviceId), webSocketSend);
  175. return (true,"");
  176. }
  177. private ZL_AlarmFlags ParseAlarmFlags(byte flagByte)
  178. {
  179. return new ZL_AlarmFlags
  180. {
  181. BloodLeak = (flagByte & 0x01) != 0,
  182. LiquidLevel = (flagByte & 0x02) != 0,
  183. Bubble = (flagByte & 0x04) != 0,
  184. ArterialPressure = (flagByte & 0x08) != 0,
  185. TransmembranePressure = (flagByte & 0x10) != 0,
  186. VenousPressure = (flagByte & 0x20) != 0,
  187. Temperature = (flagByte & 0x40) != 0,
  188. Conductivity = (flagByte & 0x80) != 0
  189. };
  190. }
  191. private string GetModeName(byte mode)
  192. {
  193. return mode switch
  194. {
  195. 10 => "Dialysis",
  196. 12 => "LowSuper",
  197. 13 => "SingleSuper",
  198. 14 => "BloodReturn",
  199. 15 => "Precharge",
  200. 16 => "SelfTest",
  201. 17 => "Disinfection",
  202. _ => "Unknown"
  203. };
  204. }
  205. private double ApplyDataWeight(ushort rawValue, int weight, byte dataId)
  206. {
  207. // 特殊处理电导值(数据标识0x09)
  208. if (dataId == 0x09 && weight > 4)
  209. return rawValue; // 按整数显示
  210. return weight switch
  211. {
  212. 0 or 15 => rawValue, // 整数
  213. > 0 and <= 4 => rawValue / Math.Pow(10, weight), // 小数处理
  214. _ => rawValue // 默认按整数处理
  215. };
  216. }
  217. private (string name, string unit) GetDataInfo(byte dataId)
  218. {
  219. var dataMap = new Dictionary<byte, (string, string)>
  220. {
  221. { 0x01, ("dehydration", "L") },//脱水1
  222. { 0x02, ("currentDehydration", "L") },//当前的脱水2
  223. { 0x03, ("dehydrationSpeed", "L/h") },//脱水速度3
  224. { 0x04, ("bloodPumpFlow", "ml/min") },//血泵流量4
  225. { 0x05, ("auxiliaryPump", "") },//辅助泵5
  226. { 0x06, ("syringe", "ml/h") },//注射器6
  227. { 0x07, ("dialysateFlow", "") },//透析液流量7
  228. { 0x08, ("dialysateTemperature", "°C") },//透析液温度8
  229. { 0x09, ("dialysateConductivity", "mS/cm") },//透析液电导9
  230. { 0x0A, ("venousPressure", "") },//静脉压力A
  231. { 0x0B, ("transmembranePressure", "") },//跨膜压力B
  232. { 0x0C, ("dialyzedTime", "min") },//已透析时间C
  233. { 0x0D, ("remainingTime", "min") },//剩余透析时间D
  234. { 0x0E, ("arterialPressure", "") },//动脉压E
  235. { 0x0F, ("sphygmomanometerHigh", "") },//血压计测量 高压F
  236. { 0x10, ("sphygmomanometerLow", "") },//血压计测量 低压10
  237. { 0x11, ("heartRate", "bpm") }//心率11
  238. };
  239. return dataMap.TryGetValue(dataId, out var info)
  240. ? info
  241. : ("Unknown", "");
  242. }
  243. }
复制代码

WebSocket服务

  1. public class WebSocketManagement
  2. {
  3. private readonly ConcurrentDictionary<string, WebSocket> _sockets = new();
  4. public async Task HandleWebSocketConnectionAsync(WebSocket webSocket)
  5. {
  6. var buffer = new byte[1024 * 4];
  7. var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
  8. if (result.MessageType == WebSocketMessageType.Text)
  9. {
  10. var deviceId = Encoding.UTF8.GetString(buffer, 0, result.Count);
  11. _sockets[deviceId.Trim()] = webSocket;
  12. Console.WriteLine("socket消息:" + deviceId);
  13. }
  14. while (webSocket.State == WebSocketState.Open)
  15. {
  16. await Task.Delay(100);
  17. }
  18. }
  19. public async Task BroadcastAsync(string socketId, dynamic data)
  20. {
  21. var json = JsonConvert.SerializeObject(data);
  22. var buffer = Encoding.UTF8.GetBytes(json);
  23. _sockets.TryGetValue(socketId, out WebSocket socket);
  24. if (socket!=null&&socket.State == WebSocketState.Open)
  25. {
  26. await socket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
  27. }
  28. else
  29. {
  30. _sockets.TryRemove(socketId, out _);
  31. }
  32. }
  33. }
复制代码

测试数据发送接收

  1. # PowerShell
  2. $data = [byte[]](0x55, 0xAA, 0x00, 0x26, 0x35, 0xB2, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00, 0x02, 0x1B)
  3. $client = New-Object System.Net.Sockets.TcpClient('localhost', 8081)
  4. $stream = $client.GetStream()
  5. $stream.Write($data, 0, $data.Length)
  6. $client.Close()
复制代码

1.png

使用 WebSocket 测试工具:

浏览器开发者工具

https://websocketking.com/

到此这篇关于.net8创建tcp服务接收数据通过websocket广播的文章就介绍到这了,更多相关.net8 tcp服务接收数据内容请搜索晓枫资讯以前的文章或继续浏览下面的相关文章希望大家以后多多支持晓枫资讯!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

  • 打卡等级:无名新人
  • 打卡总天数:1
  • 打卡月天数:0
  • 打卡总奖励:9
  • 最近打卡:2025-09-02 03:00:40
等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
22
积分
6
注册时间
2025-1-11
最后登录
2025-9-2

发表于 2025-10-6 21:09:18 | 显示全部楼层
路过,支持一下
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1楼
2楼

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表