From 3d46c7e7b7b375e837f6ff47129a0428c57f70c2 Mon Sep 17 00:00:00 2001 From: gcw_MV9p2JJN Date: Sat, 20 Dec 2025 13:23:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=8B=E8=A1=A8=E9=80=9A=E8=AE=AF=E5=AE=8C?= =?UTF-8?q?=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Infrastructure/Helper/SocketServerHelper.cs | 255 ++++++++++++++++++ ZR.Admin.WebApi/Program.cs | 9 + .../background/SocketBackgroundService.cs | 36 +++ ZR.Service/Utils/MyMatchPush/Watchup.cs | 54 ++++ 4 files changed, 354 insertions(+) create mode 100644 Infrastructure/Helper/SocketServerHelper.cs create mode 100644 ZR.Admin.WebApi/background/SocketBackgroundService.cs create mode 100644 ZR.Service/Utils/MyMatchPush/Watchup.cs diff --git a/Infrastructure/Helper/SocketServerHelper.cs b/Infrastructure/Helper/SocketServerHelper.cs new file mode 100644 index 00000000..353dd267 --- /dev/null +++ b/Infrastructure/Helper/SocketServerHelper.cs @@ -0,0 +1,255 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; + +namespace Infrastructure +{ + + + /// + /// Socket 服务端工具类(你的“接受端”) + /// 功能:监听端口,与网关(客户端)建立长连接,支持收发字节数据(byte[]) + /// + public class SocketGatewayServer : IDisposable + { + private TcpListener _listener; + private bool _isRunning; + private Thread _acceptThread; + + private TcpClient _connectedClient; + private NetworkStream _clientStream; + private bool _clientConnected; + + private readonly string _listenIp; + private readonly int _listenPort; + + /// + /// 构造函数 + /// + /// 监听的IP,如 "127.0.0.1" 或 "0.0.0.0" + /// 监听的端口,如 8888 + public SocketGatewayServer(string listenIp = "127.0.0.1", int listenPort = 8888) + { + _listenIp = listenIp; + _listenPort = listenPort; + } + + /// + /// 启动服务端,开始监听网关(客户端)连接 + /// + public void Start() + { + if (_isRunning) return; + + try + { + IPAddress ipAddress = string.IsNullOrEmpty(_listenIp) || _listenIp == "0.0.0.0" + ? IPAddress.Any + : IPAddress.Parse(_listenIp); + + _listener = new TcpListener(ipAddress, _listenPort); + _listener.Start(); + _isRunning = true; + + Console.WriteLine($"Socket 服务端已启动,正在监听 [{_listenIp}]:{_listenPort}"); + + _acceptThread = new Thread(AcceptClient); + _acceptThread.IsBackground = true; + _acceptThread.Start(); + } + catch (Exception ex) + { + Console.WriteLine($"❌ 启动服务端失败: {ex.Message}"); + Stop(); + } + } + + /// + /// 等待并接受一个客户端(网关)连接 + /// + private void AcceptClient() + { + try + { + while (_isRunning) + { + Console.WriteLine(" 等待网关(客户端)连接..."); + + TcpClient client = _listener.AcceptTcpClient(); // 阻塞直到客户端连接 + Console.WriteLine($"网关(客户端)已连接: {client.Client.RemoteEndPoint}"); + StartReceiving(); + + lock (this) + { + // 断开之前的连接(如果存在) + _connectedClient?.Close(); + _connectedClient = client; + _clientStream = client.GetStream(); + _clientConnected = true; + + Console.WriteLine("已建立长连接,可以开始收发字节数据"); + } + } + } + catch (Exception ex) + { + if (_isRunning) + Console.WriteLine($"接受客户端时出错: {ex.Message}"); + } + } + + /// + /// 向网关(客户端)发送字节数据(byte[]) + /// + /// 要发送的字节数据 + /// 是否发送成功 + public bool SendToGateway(byte[] data) + { + if (!_clientConnected || _clientStream == null || _connectedClient == null) + { + Console.WriteLine(" 未连接到网关,无法发送数据"); + return false; + } + + try + { + _clientStream.Write(data, 0, data.Length); + Console.WriteLine($" 已发送 {data.Length} 字节数据给网关"); + return true; + } + catch (Exception ex) + { + Console.WriteLine($"发送数据失败: {ex.Message}"); + DisconnectClient(); + return false; + } + } + + /// + /// (可选)接收来自网关的字节数据 + /// + public void StartReceiving() + { + Thread receiveThread = new Thread(ReceiveLoop); + receiveThread.IsBackground = true; + receiveThread.Start(); + } + + private void ReceiveLoop() + { + byte[] buffer = new byte[4096]; // 可根据需求调整缓冲区大小 + + try + { + while (_clientConnected && _connectedClient != null && _clientStream != null) + { + if (_clientStream.DataAvailable) + { + int bytesRead = _clientStream.Read(buffer, 0, buffer.Length); + if (bytesRead > 0) + { + byte[] receivedData = new byte[bytesRead]; + Buffer.BlockCopy(buffer, 0, receivedData, 0, bytesRead); + + Console.WriteLine($"收到来自网关的 {bytesRead} 字节数据"); + + // 🔧 新增:将收到的 byte[] 使用 GBK 编码转换为字符串 + // string receivedText = Encoding.GetEncoding("GBK").GetString(receivedData); + + // Console.WriteLine($"GBK 解码后的报文内容: {receivedText}"); + PrintBytesAsHex(receivedData); + // TODO: 你可以在这里进一步处理 receivedText 或 receivedData + } + } + else + { + Thread.Sleep(100); // 避免空转消耗 CPU + } + } + } + catch (Exception ex) + { + Console.WriteLine($"接收数据时出错: {ex.Message}"); + } + finally + { + Console.WriteLine("接收线程已退出"); + } + } + /// + /// 将字节数组转换为十六进制字符串并打印 + /// + /// 要转换的字节数组 + static void PrintBytesAsHex(byte[] bytes) + { + if (bytes == null || bytes.Length == 0) + { + Console.WriteLine("字节数组为空或长度为0。"); + return; + } + + // 使用 StringBuilder 或直接拼接,这里使用 String.Join 和格式化 + string hexString = BitConverter.ToString(bytes).Replace("-", " "); // 可选:用空格分隔每个字节 + // 或者不替换,就是用 "-" 分隔:string hexString = BitConverter.ToString(bytes); + + Console.WriteLine("字节数据的十六进制表示为:"+ hexString); + + } + + private void DisconnectClient() + { + lock (this) + { + _clientConnected = false; + _clientStream?.Close(); + _connectedClient?.Close(); + _clientStream = null; + _connectedClient = null; + Console.WriteLine("🔌 已断开与网关的连接"); + } + } + + public void Stop() + { + _isRunning = false; + DisconnectClient(); + _listener?.Stop(); + Console.WriteLine("🛑 Socket 服务端已停止"); + } + + public void Dispose() + { + Stop(); + GC.SuppressFinalize(this); + } + + + /// + /// 计算多个 byte[] 数组中所有字节之和,然后对 256 取余,返回校验字节(0~255) + /// + /// 多个 byte[] 数组,可以是任意数量 + /// 校验和,一个字节(0~255),即:所有字节之和 % 256 + public static byte CalculateChecksum(params byte[][] byteArrays) + { + int sum = 0; + + // 遍历每一个 byte[] 数组 + foreach (byte[] bytes in byteArrays) + { + if (bytes == null) + continue; // 如果某个数组为空,跳过(你也可以选择抛异常) + + // 累加该数组中的每一个字节 + foreach (byte b in bytes) + { + sum += b; // byte 自动提升为 int,安全累加 + } + } + + // 计算校验和:总和 % 256,然后转为 byte(范围保证是 0~255) + return (byte)(sum % 256); + } + } +} diff --git a/ZR.Admin.WebApi/Program.cs b/ZR.Admin.WebApi/Program.cs index 61138fae..58a590e7 100644 --- a/ZR.Admin.WebApi/Program.cs +++ b/ZR.Admin.WebApi/Program.cs @@ -112,6 +112,15 @@ builder.Services.Configure((o) => { o.SuppressModelStateInvalidFilter = true; }); +// 注册 SocketGatewayServer 为 Singleton(单例,整个应用生命周期一个实例) +builder.Services.AddSingleton(provider => +{ + var server = new SocketGatewayServer("192.168.50.163", 4021); // 你可以按需修改 IP 和端口 + server.Start(); // 项目启动时立即启动监听 + //server.StartReceiving(); + + return server; +}); var app = builder.Build(); InternalApp.ServiceProvider = app.Services; InternalApp.Configuration = builder.Configuration; diff --git a/ZR.Admin.WebApi/background/SocketBackgroundService.cs b/ZR.Admin.WebApi/background/SocketBackgroundService.cs new file mode 100644 index 00000000..910f5fe1 --- /dev/null +++ b/ZR.Admin.WebApi/background/SocketBackgroundService.cs @@ -0,0 +1,36 @@ + +using DOAN.ServiceCore.MyMatchPush; +using Infrastructure; +using Microsoft.Extensions.Logging; + +namespace ZR.Admin.WebApi.background +{ + public class SocketBackgroundService : BackgroundService + { + private readonly SocketGatewayServer _socketGateway; + private readonly ILogger _logger; + + public SocketBackgroundService(SocketGatewayServer socketGateway, ILogger logger) + { + _socketGateway = socketGateway; + _logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + try + { + // 等待服务完全启动 + await Task.Delay(1000, stoppingToken); + + // 测试Socket推送功能 + var result = Watchup.StartPush("测试Socket推送功能", _socketGateway); + _logger.LogInformation($"Socket推送测试结果: {result}"); + } + catch (Exception ex) + { + _logger.LogError(ex, "Socket推送测试失败"); + } + } + } +} diff --git a/ZR.Service/Utils/MyMatchPush/Watchup.cs b/ZR.Service/Utils/MyMatchPush/Watchup.cs new file mode 100644 index 00000000..7d04fcd3 --- /dev/null +++ b/ZR.Service/Utils/MyMatchPush/Watchup.cs @@ -0,0 +1,54 @@ + +using Infrastructure; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DOAN.ServiceCore.MyMatchPush +{ + public class Watchup + { + private static int PackageSort = 1; + public static bool StartPush(string message, SocketGatewayServer _socketGateway) + { + + //发送报警信息 + //包头 + byte[] packageHeader = { 0x5A }; + + //包序号 + byte[] packsort = { (byte)PackageSort }; + PackageSort++; + + if (PackageSort > 255) + { + PackageSort = 1; + } + // 地址 + byte[] IPaddress = { 0xff, 0xff, 0xff, 0xff }; + //命令码 + byte[] CommandCode = { 0xE0 }; + //数据长 + //string message = $"产线:{record.LineCode},\n故障类型:{record.FaultDict},\n故障内容:{record.FaultContext},\n报警人:{record.AskPerson}"; + // string message = $"产线:{record.LineCode}"; + byte[] MessageData = Encoding.GetEncoding("GBK").GetBytes(message); + byte[] MessageLength = { (byte)MessageData.Length }; + // === 先拼接除 CRC 之外的所有部分 === + byte[] dataWithoutCRC = + packageHeader + .Concat(packsort) + .Concat(IPaddress) + .Concat(CommandCode) + .Concat(MessageLength) + .Concat(MessageData).ToArray(); + + //CRC : 校验和,CRC 前面所有数据之和除 256 的余数 + byte[] CRC = { SocketGatewayServer.CalculateChecksum(dataWithoutCRC) }; + byte[] Body = dataWithoutCRC.Concat(CRC).ToArray(); + + return _socketGateway.SendToGateway(Body); + } + } +}