Files
Ayay/SHH.CameraService/Core/NetSenders/NetMqSenderWorker.cs

84 lines
3.2 KiB
C#
Raw Normal View History

using Microsoft.Extensions.Hosting;
using NetMQ;
using NetMQ.Sockets;
namespace SHH.CameraService;
/// <summary>
/// NetMQ 发送工作者
/// 职责:从指定目标的 VideoDataChannel 读取 Payload通过 ZeroMQ 发送出去
/// </summary>
public class NetMqSenderWorker : BackgroundService
{
private readonly StreamTarget _target;
// 构造函数注入特定的目标对象 (由 Program.cs 的工厂方法提供)
public NetMqSenderWorker(StreamTarget target)
{
_target = target;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// 增加重启保护
while (!stoppingToken.IsCancellationRequested)
{
try
{
Console.WriteLine($"[NetMqSender] 连接至: {_target.Config.Endpoint}");
using var clientSocket = new PublisherSocket();
clientSocket.Options.SendHighWatermark = 1000;
// 关键:增加 TCP 保活,防止防火墙静默断开长连接
clientSocket.Options.TcpKeepalive = true;
clientSocket.Options.TcpKeepaliveIdle = TimeSpan.FromSeconds(5);
clientSocket.Connect(_target.Config.Endpoint);
int frameCount = 0;
// 使用更稳健的读取方式
await foreach (var payload in _target.Channel.Reader.ReadAllAsync(stoppingToken))
{
try
{
// 1. 构造消息 (内部执行了 MessagePack 序列化)
var msg = payload.ToNetMqMessage();
// 2. 发送
bool sent = clientSocket.TrySendMultipartMessage(msg);
if (!sent)
{
Console.WriteLine($"[NetMqSender] 发送缓冲区满,丢弃帧: {payload.CameraId}");
// ★ 如果没有发送成功,建议显式清理消息帧,防止内存滞留
msg.Clear();
}
else
{
frameCount++;
if (frameCount % 100 == 0)
Console.WriteLine($"[NetMqSender] 已搬运 100 帧至缓冲区.");
}
}
catch (Exception ex)
{
Console.WriteLine($"[NetMqSender] 内部循环异常: {ex.Message}");
}
}
}
catch (OperationCanceledException) { break; }
catch (Exception ex)
{
// ★★★ 核心改进:捕获异常并等待重试 ★★★
// 防止因为一次内存溢出或网络波动导致整个 BackgroundService 永久停止
Console.WriteLine($"[NetMqSender] 发生致命异常5秒后尝试重建连接: {ex.Message}");
await Task.Delay(5000, stoppingToken);
}
finally
{
// 确保每次循环退出(无论是异常还是正常)都清理环境
NetMQConfig.Cleanup(false);
}
}
}
}