手表通讯完毕

This commit is contained in:
gcw_MV9p2JJN
2025-12-20 13:23:44 +08:00
parent 16168ed598
commit 3d46c7e7b7
4 changed files with 354 additions and 0 deletions

View File

@@ -0,0 +1,255 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Infrastructure
{
/// <summary>
/// Socket 服务端工具类(你的“接受端”)
/// 功能监听端口与网关客户端建立长连接支持收发字节数据byte[]
/// </summary>
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;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="listenIp">监听的IP如 "127.0.0.1" 或 "0.0.0.0"</param>
/// <param name="listenPort">监听的端口,如 8888</param>
public SocketGatewayServer(string listenIp = "127.0.0.1", int listenPort = 8888)
{
_listenIp = listenIp;
_listenPort = listenPort;
}
/// <summary>
/// 启动服务端,开始监听网关(客户端)连接
/// </summary>
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();
}
}
/// <summary>
/// 等待并接受一个客户端(网关)连接
/// </summary>
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}");
}
}
/// <summary>
/// 向网关客户端发送字节数据byte[]
/// </summary>
/// <param name="data">要发送的字节数据</param>
/// <returns>是否发送成功</returns>
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;
}
}
/// <summary>
/// (可选)接收来自网关的字节数据
/// </summary>
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("接收线程已退出");
}
}
/// <summary>
/// 将字节数组转换为十六进制字符串并打印
/// </summary>
/// <param name="bytes">要转换的字节数组</param>
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);
}
/// <summary>
/// 计算多个 byte[] 数组中所有字节之和,然后对 256 取余返回校验字节0~255
/// </summary>
/// <param name="byteArrays">多个 byte[] 数组,可以是任意数量</param>
/// <returns>校验和一个字节0~255所有字节之和 % 256</returns>
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);
}
}
}

View File

@@ -112,6 +112,15 @@ builder.Services.Configure<ApiBehaviorOptions>((o) =>
{
o.SuppressModelStateInvalidFilter = true;
});
// 注册 SocketGatewayServer 为 Singleton单例整个应用生命周期一个实例
builder.Services.AddSingleton<SocketGatewayServer>(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;

View File

@@ -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<SocketBackgroundService> _logger;
public SocketBackgroundService(SocketGatewayServer socketGateway, ILogger<SocketBackgroundService> 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推送测试失败");
}
}
}
}

View File

@@ -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);
}
}
}