diff --git a/SHH.AIServer/SHH.AIServer.slnx b/SHH.AIServer/SHH.AIServer.slnx deleted file mode 100644 index 8e3933d..0000000 --- a/SHH.AIServer/SHH.AIServer.slnx +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/SHH.AIServer/SHH.AIServer/Program.cs b/SHH.AIServer/SHH.AIServer/Program.cs deleted file mode 100644 index 744ccbe..0000000 --- a/SHH.AIServer/SHH.AIServer/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SHH.AIServer -{ - internal class Program - { - static void Main(string[] args) - { - Console.WriteLine("Hello, World!"); - } - } -} diff --git a/SHH.AIServer/SHH.AIServer/SHH.AIServer.csproj b/SHH.AIServer/SHH.AIServer/SHH.AIServer.csproj deleted file mode 100644 index 2150e37..0000000 --- a/SHH.AIServer/SHH.AIServer/SHH.AIServer.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - Exe - net8.0 - enable - enable - - - diff --git a/SHH.NetMQ/DistributorServer.cs b/SHH.NetMQ/DistributorServer.cs deleted file mode 100644 index 7bf6f46..0000000 --- a/SHH.NetMQ/DistributorServer.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using NetMQ; -using NetMQ.Sockets; -using SHH.Contracts; - -namespace SHH.NetMQ -{ - /// - /// 视频分发服务端 (Publisher) - /// 特性:非阻塞、防内存溢出 - /// - public class DistributorServer : IDisposable - { - private PublisherSocket _pubSocket; - private readonly object _lock = new object(); - - // 配置:高水位限制 (HWM) - // 假设 25fps,设置 50 意味着内存只缓存 2 秒的视频。 - // 如果断网超过 2 秒,新来的视频帧直接丢弃,优先保证恢复后的实时性。 - private const int HWM_LIMIT = 50; - - public DistributorServer(string connectionString) - { - _pubSocket = new PublisherSocket(); - - // 1. 设置发送缓冲区大小 (防爆内存关键) - _pubSocket.Options.SendHighWatermark = HWM_LIMIT; - - // 2. 绑定地址 (如 tcp://*:5555) - _pubSocket.Bind(connectionString); - } - - public void Broadcast(VideoPayload payload) - { - if (payload == null) return; - - // 补充发送时间 - payload.DispatchTimestamp = DateTime.Now.Ticks; - - // 准备数据帧 - string jsonMeta = payload.GetMetadataJson(); - byte[] rawBytes = payload.OriginalImageBytes ?? new byte[0]; - byte[] targetBytes = payload.TargetImageBytes ?? new byte[0]; - - // 使用 NetMQMessage 封装多帧消息 - // 这样比手动调三次 Send 更容易管理原子性 - var msg = new NetMQMessage(); - msg.Append(jsonMeta); // 第1帧 - msg.Append(rawBytes); // 第2帧 - msg.Append(targetBytes); // 第3帧 - - lock (_lock) - { - // 3. 非阻塞发送 (核心防卡死代码) - // TimeSpan.Zero 表示:如果缓冲区满了或者发不出去,立即放弃,不等待,返回 false - // 这样你的主线程(海康SDK回调)永远不会被卡住 - bool sent = _pubSocket.TrySendMultipartMessage(TimeSpan.Zero, msg); - - if (!sent) - { - // 这里可以打个日志:Console.WriteLine("警告:网络拥堵,丢帧中..."); - } - } - } - - public void Dispose() - { - _pubSocket?.Dispose(); - } - } -} \ No newline at end of file diff --git a/SHH.NetMQ/ForwarderClient.cs b/SHH.NetMQ/ForwarderClient.cs deleted file mode 100644 index 3ea4c73..0000000 --- a/SHH.NetMQ/ForwarderClient.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; -using NetMQ; -using NetMQ.Sockets; -using Newtonsoft.Json; -using SHH.Contracts; - -namespace SHH.NetMQ -{ - /// - /// 视频转发客户端 (Fusion Version) - /// 核心特性: - /// 1. 主动推送 (Push Mode) - /// 2. 带宽保护 (HWM=2, 拥堵丢弃) - /// 3. 高效传输 (Multipart, 避免 Base64) - /// 4. 线程安全 (Lock 保护) - /// - public class ForwarderClient : IDisposable - { - #region --- 字段与配置 --- - - private readonly PushSocket _pushSocket; - private readonly object _lock = new object(); - private bool _isDisposed = false; - - // ★★★ 核心策略:高水位线 (High Water Mark) ★★★ - // 设置为 2,意味着内存队列中最多只允许堆积 2 帧图片。 - // 第 3 帧到来时,如果网速不够发不出去,这第 3 帧会被直接丢弃。 - // 收益:永远只发最新的图,永远不挤占物理带宽,彻底杜绝延迟累积。 - private const int HWM_LIMIT = 2; - - #endregion - - #region --- 构造函数 --- - - public ForwarderClient(string remoteAddress) - { - if (string.IsNullOrWhiteSpace(remoteAddress)) - throw new ArgumentNullException(nameof(remoteAddress)); - - try - { - _pushSocket = new PushSocket(); - - // 1. 配置防堆积策略 (必须在 Connect 之前设置) - _pushSocket.Options.SendHighWatermark = HWM_LIMIT; - - // 2. 配置 Linger (逗留时间) - // 设为 0 表示:当 Dispose 时,如果队列里还有没发完的数据,直接扔掉,不要等待。 - // 避免关程序时卡死。 - _pushSocket.Options.Linger = TimeSpan.Zero; - - // 3. 建立连接 (NetMQ 会自动在后台处理断线重连) - _pushSocket.Connect(remoteAddress); - } - catch (Exception ex) - { - // 构造函数异常通常是致命的,向上抛出让启动流程感知 - throw new InvalidOperationException($"[ForwarderClient] 初始化失败: {ex.Message}", ex); - } - } - - #endregion - - #region --- 核心发送逻辑 --- - - /// - /// 推送视频帧 (非阻塞,线程安全) - /// - /// 视频帧载荷 - public void Push(VideoPayload payload) - { - if (_isDisposed || payload == null) return; - - try - { - // 1. 准备多帧消息 (Multipart Message) - // 这种方式比把 byte[] 转成 Base64 字符串塞进 JSON 要高效得多 (减少 33% 体积,且无 GC 压力) - var msg = new NetMQMessage(); - - // --- 第一帧:元数据 (JSON) --- - // 我们使用匿名对象来生成 JSON,刻意排除 byte[] 数组 - // 这样生成的 JSON 非常小,只有几十字节 - var metaJson = JsonConvert.SerializeObject(new - { - payload.CameraId, - payload.CaptureTimestamp, - payload.DispatchTimestamp, - payload.OriginalWidth, - payload.OriginalHeight, - // 如果有订阅者ID列表也带上 - payload.SubscriberIds - }); - msg.Append(metaJson); - - // --- 第二帧:原始图像数据 (Binary) --- - // 直接追加二进制数据,实现 Zero-Copy (零拷贝) - // NetMQ 底层会直接搬运这段内存,不会产生临时的 Base64 字符串 - msg.Append(payload.OriginalImageBytes ?? Array.Empty()); - - // 2. 线程安全发送 - // NetMQ 的 Socket 实例不是线程安全的,多线程同时调用 Push 必须加锁 - lock (_lock) - { - // 3. 非阻塞尝试发送 (TrySend) - // TimeSpan.Zero 表示:如果队列满了 (超过 HWM),立刻返回 false,不要等待。 - // 这实现了 "拥堵即丢弃" 的保护机制。 - if (!_pushSocket.TrySendMultipartMessage(TimeSpan.Zero, msg)) - { - // 返回 false 说明触发了 HWM 保护 - // 此时我们选择静默丢弃,或者仅在调试模式下打印日志 - // Console.WriteLine($"[Drop] 网络拥堵,丢弃帧: {payload.CameraId}"); - } - } - } - catch (Exception ex) - { - // 发送过程中的异常 (通常是 ObjectDisposedException 或 NetMQException) - // 捕获它以防止单个帧的发送失败导致整个服务崩溃 - Console.WriteLine($"[ForwarderClient] 推送异常: {ex.Message}"); - } - } - - #endregion - - #region --- 资源释放 --- - - public void Dispose() - { - if (_isDisposed) return; - - lock (_lock) - { - if (_isDisposed) return; - _isDisposed = true; - - try - { - _pushSocket?.Close(); - _pushSocket?.Dispose(); - } - catch { /* 忽略关闭时的异常 */ } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/SHH.NetMQ/SHH.NetMQ.csproj b/SHH.NetMQ/SHH.NetMQ.csproj deleted file mode 100644 index ebb7747..0000000 --- a/SHH.NetMQ/SHH.NetMQ.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - netstandard2.0 - $(NoWarn);NU1701 - - - - - - - - - - -