using NetMQ; using NetMQ.Sockets; using SHH.Contracts; // 引用你 ExportCode_Dump2.txt 里的契约 namespace SHH.CameraDashboard { /// /// 视频推流接收服务 (服务端模式) /// 职责:监听本地端口,被动接收来自采集端的 Push 数据流 /// public class VideoPushServer : IDisposable { private PullSocket? _pullSocket; private bool _isRunning; private Task? _listenTask; // 当收到完整视频帧时触发,UI 层订阅此事件来更新画面 // VideoPayload 定义在 ExportCode_Dump2.txt 中 public event Action? OnFrameReceived; /// /// 启动监听 /// /// 本机开放的监听端口 (例如 6000) public void Start(int port) { if (_isRunning) return; try { _pullSocket = new PullSocket(); // 1. 设置高水位 (HWM),防止渲染不及导致内存溢出 // 与发送端 ForwarderClient 的 HWM_LIMIT = 50 保持策略一致 _pullSocket.Options.ReceiveHighWatermark = 50; // 2. 【核心】绑定本地端口 (Bind),等待别人连我 // 允许局域网内任何 IP 推送数据过来 _pullSocket.Bind($"tcp://*:{port}"); _isRunning = true; // 3. 开启后台接收线程 _listenTask = Task.Run(ReceiveLoop); System.Diagnostics.Debug.WriteLine($"[PushServer] 服务已启动,正在监听端口: {port}..."); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"[PushServer] 启动失败: {ex.Message}"); } } private void ReceiveLoop() { while (_isRunning && _pullSocket != null) { try { // 1. 接收多帧消息 (超时控制以便能优雅退出) NetMQMessage? msg = null; if (!_pullSocket.TryReceiveMultipartMessage(TimeSpan.FromMilliseconds(500), ref msg)) { continue; } // 2. 协议校验:必须包含 3 帧 (JSON + Raw + Target) // 对应 ExportCode_Dump1.txt 中 ForwarderClient.Push 的 msg.Append 顺序 if (msg == null || msg.FrameCount < 3) continue; // 3. 解析数据帧 string jsonMeta = msg[0].ConvertToString(); byte[] originalBytes = msg[1].Buffer; byte[] targetBytes = msg[2].Buffer; // 4. 反序列化元数据 // 使用你 Core/JsonHelper.cs 里的稳健反序列化 var payload = JsonHelper.Deserialize(jsonMeta); if (payload != null) { // 5. 组装二进制数据 (因为传输时是分离的) payload.OriginalImageBytes = originalBytes; payload.TargetImageBytes = targetBytes; // 6. 触发事件 (抛出给 ViewModel) OnFrameReceived?.Invoke(payload); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"[PushServer] 接收异常: {ex.Message}"); } } } public void Stop() { _isRunning = false; _pullSocket?.Close(); _pullSocket?.Dispose(); _pullSocket = null; } public void Dispose() { Stop(); } } }