using MessagePack; using Microsoft.Extensions.Hosting; using NetMQ; using NetMQ.Sockets; using SHH.CameraSdk; using SHH.Contracts; using System.Text; namespace SHH.CameraService; public class CommandClientWorker : BackgroundService { private readonly ServiceConfig _config; private readonly CommandDispatcher _dispatcher; private readonly InterceptorPipeline _pipeline; // 管理多个 Socket private readonly List _sockets = new(); private NetMQPoller? _poller; public CommandClientWorker( ServiceConfig config, CommandDispatcher dispatcher, InterceptorPipeline pipeline) { _config = config; _dispatcher = dispatcher; _pipeline = pipeline; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await Task.Yield(); if (!_config.ShouldConnect || _config.CommandEndpoints.Count == 0) return; // 1. 建立连接 (但不立即启动 Poller) _poller = new NetMQPoller(); foreach (var ep in _config.CommandEndpoints) { try { var socket = new DealerSocket(); // 建议加上 Socket 索引或 UUID 以防服务端认为 Identity 冲突 // 或者保持原样,取决于服务端逻辑。通常同一个 AppId 连不同 Server 是没问题的。 socket.Options.Identity = Encoding.UTF8.GetBytes(_config.AppId); socket.Connect(ep.Uri); socket.ReceiveReady += OnSocketReceiveReady; _sockets.Add(socket); _poller.Add(socket); Console.WriteLine($"[指令] 建立通道: {ep.Uri}"); } catch (Exception ex) { Console.WriteLine($"[指令] 连接异常: {ex.Message}"); } } if (_sockets.Count == 0) return; // ================================================================= // 2. 发送注册包 (在 Poller 启动前发送,绝对线程安全) // ================================================================= var registerPayload = new RegisterPayload { Protocol = ProtocolHeaders.ServerRegister, InstanceId = _config.AppId, ProcessId = Environment.ProcessId, Version = "1.0.0", ServerIp = "127.0.0.1", WebApiPort = _config.BasePort, StartTime = DateTime.Now }; try { byte[] regData = MessagePackSerializer.Serialize(registerPayload); var ctx = await _pipeline.ExecuteSendAsync(ProtocolHeaders.ServerRegister, regData); if (ctx != null) { foreach (var socket in _sockets) { // 此时 Poller 还没跑,主线程发送是安全的 socket.SendMoreFrame(ctx.Protocol).SendFrame(ctx.Data); } Console.WriteLine($"[指令] 注册包已广播至 {_sockets.Count} 个目标"); } } catch (Exception ex) { Console.WriteLine($"[指令] 注册失败: {ex.Message}"); } // ================================================================= // 3. 绑定 ACK 逻辑 // ================================================================= // 关键修正:直接使用 async void,不要包裹在 Task.Run 中! // 因为 OnResponseReady 是由 Dispatcher 触发的,而 Dispatcher 是由 Poller 线程触发的。 // 所以这里就在 Poller 线程内,可以直接操作 Socket。 _dispatcher.OnResponseReady += async (result) => { try { byte[] resultBytes = MessagePackSerializer.Serialize(result); var ctx = await _pipeline.ExecuteSendAsync(ProtocolHeaders.CommandResult, resultBytes); if (ctx != null) { foreach (var socket in _sockets) { socket.SendMoreFrame(ctx.Protocol).SendFrame(ctx.Data); } Console.WriteLine($"[指令] ACK 已广播 (ID: {result.RequestId})"); } } catch (Exception ex) { Console.WriteLine($"[ACK] 发送失败: {ex.Message}"); } }; // ================================================================= // 4. 启动 Poller (开始监听接收) // ================================================================= _poller.RunAsync(); // 阻塞直到取消 while (!stoppingToken.IsCancellationRequested) { await Task.Delay(1000, stoppingToken); } // 清理 _poller.Stop(); _poller.Dispose(); foreach (var s in _sockets) s.Dispose(); } private async void OnSocketReceiveReady(object? sender, NetMQSocketEventArgs e) { // 这里的代码运行在 Poller 线程 NetMQMessage incomingMsg = new NetMQMessage(); if (e.Socket.TryReceiveMultipartMessage(ref incomingMsg)) { if (incomingMsg.FrameCount >= 2) { try { string rawProtocol = incomingMsg[0].ConvertToString(); byte[] rawData = incomingMsg[1].ToByteArray(); var ctx = await _pipeline.ExecuteReceiveAsync(rawProtocol, rawData); if (ctx != null) { // DispatchAsync 会同步触发 OnResponseReady, // 从而在同一个线程内完成 ACK 发送,线程安全且高效。 await _dispatcher.DispatchAsync(ctx.Protocol, ctx.Data); } } catch (Exception ex) { Console.WriteLine($"[指令] 处理异常: {ex.Message}"); } } } } }