Files
Ayay/SHH.MjpegPlayer/Bootstrapper.cs

190 lines
6.4 KiB
C#
Raw Permalink Normal View History

using Ayay.SerilogLogs;
using CoreWCF;
using CoreWCF.Configuration;
using CoreWCF.Description;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Player.MJPEG;
using Serilog;
using System.Diagnostics;
using System.Net;
namespace SHH.MjpegPlayer
{
public static class Bootstrapper
{
private static readonly ILogger _sysLog = Log.ForContext("SourceContext", LogModules.Core);
#region LoadConfig
/// <summary>
/// 加载配置文件
/// </summary>
/// <returns></returns>
public static MjpegConfig LoadConfig()
{
try
{
// [修复] 路径处理脆弱性:使用 BaseDirectory 拼接,避免相对路径替换的风险
// 生产环境:强制使用绝对路径确保能找到配置文件
if (!Debugger.IsAttached)
{
if (!string.IsNullOrEmpty(JsonConfigUris.MjpegConfig))
JsonConfigUris.MjpegConfig = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Path.GetFileName(JsonConfigUris.MjpegConfig));
}
// 加载配置文件
MjpegConfig? cfg = null;
if (!string.IsNullOrEmpty(JsonConfigUris.MjpegConfig))
{
cfg = JsonConfig.Load<MjpegConfig>(JsonConfigUris.MjpegConfig);
if (cfg == null)
{
cfg = new MjpegConfig();
JsonConfig.Save(cfg, JsonConfigUris.MjpegConfig, "MjpegServer配置项");
_sysLog.Warning("未找到配置文件,已生成默认配置: {Path}", JsonConfigUris.MjpegConfig);
}
MjpegStatics.Cfg = cfg;
}
if (cfg == null)
cfg = new MjpegConfig();
return cfg;
}
catch(Exception ex)
{
_sysLog.Error("加载配置文件失败.");
Console.ReadLine();
return new MjpegConfig();
}
}
#endregion
#region ValidateEnvironment
/// <summary>
/// 检查 IP 与端口
/// </summary>
public static void ValidateEnvironment()
{
var cfg = MjpegStatics.Cfg;
// IP 地址检查
IPAddress? ipAddress;
if (!IPAddress.TryParse(cfg.SvrMjpegIp, out ipAddress))
{
ipAddress = IPAddress.Any;
_sysLog.Warning("配置的 IP 地址非法,将使用 IPAddress. Any: {Ip}.", cfg.SvrMjpegIp);
}
// 端口检查 => Wcf 接收图片接口
var portsToCheck = new List<int> { cfg.WcfPushImagePort, cfg.SvrMjpegPortBegin, cfg.SvrMjpegPortEnd };
foreach (var port in portsToCheck)
{
if (!port.IsServerPort())
{
_sysLog.Error("端口配置无效, Port: {Port}.", port);
ExitApp($"端口配置无效, Port: {port}");
}
}
// 端口检查 => Mjpeg 服务端口
if (!cfg.SvrMjpegPortBegin.IsServerPort())
{
_sysLog.Fatal("WCF 接收端口被占用, Port:{Port}.", cfg.WcfPushImagePort);
// 退出应用
ExitApp("端口占用.");
}
// [修复] 循环逻辑错误:将 < 改为 <=,确保最后一个端口也被检测
for (var i = cfg.SvrMjpegPortBegin; i <= cfg.SvrMjpegPortEnd; i++)
{
if (!i.PortOccupiedProc())
{
// 退出应用
_sysLog.Fatal("MJPEG 监听端口被占用, Port:{Port}", i);
ExitApp($"MJPEG 监听端口被占用, Port:{i}");
}
}
}
#endregion
#region StartWcfEngine
/// <summary>
/// 内部 WCF 引擎初始化 (CoreWCF)
/// </summary>
public static void StartWcfEngine(MjpegConfig cfg)
{
// Optimized: 内存监控提升
MemoryWatchdog.Start(300, 2048);
var builder = WebApplication.CreateBuilder();
builder.WebHost.UseUrls($"http://*:{cfg.WcfPushImagePort}");
// 托管日志
builder.Host.UseSerilog(_sysLog);
builder.Services.AddServiceModelServices();
builder.Services.AddServiceModelMetadata();
builder.Services.AddSingleton<IServiceBehavior, UseRequestHeadersForMetadataAddressBehavior>()
.AddSingleton<MjpegImagesService>();
var app = builder.Build();
var wsBinding = new WSHttpBinding(SecurityMode.None);
wsBinding.MaxReceivedMessageSize = cfg.SvrPushImageMaxRecMsgSize;
// Modified: [原因] 强制转换 IApplicationBuilder 修复 UseServiceModel 的二义性
((IApplicationBuilder)app).UseServiceModel(serviceBuilder =>
{
serviceBuilder.AddService<MjpegImagesService>(opt =>
{
opt.BaseAddresses.Add(new Uri($"http://0.0.0.0:{cfg.WcfPushImagePort}"));
})
.AddServiceEndpoint<MjpegImagesService, IMjpegImagesService>(
wsBinding,
$"/{cfg.SvrNamePushImage}",
new Uri($"http://0.0.0.0:{cfg.WcfPushImagePort}/{cfg.SvrNamePushImage}")
);
});
// 关闭元数据暴露增强安全性
var meta = app.Services.GetRequiredService<ServiceMetadataBehavior>();
meta.HttpGetEnabled = false;
meta.HttpsGetEnabled = false;
Task.Run(() => app.Run());
}
#endregion
#region ExitApp
/// <summary>
/// 应用程序退出
/// </summary>
/// <param name="exitMsg"></param>
/// <param name="waitSeconds"></param>
public static void ExitApp(string exitMsg, int waitSeconds = 5)
{
// [修复] 尝试停止所有 MjpegServer 监听
try { MjpegServer.StopAll(); } catch { }
var iSleep = waitSeconds * 2;
for (var i = 0; i < iSleep; i++)
{
Thread.Sleep(500);
}
// 退出程序
Environment.Exit(0);
}
#endregion
}
}