You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kev/Drawer/UCDraw/GeoSigmaDrawLib/LicenseServerConnection.cs

240 lines
9.1 KiB
C#

1 month ago
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace GeoSigmaDrawLib
{
public class LicenseServerConnection
{
public delegate void LicenseInvalidateEventHandler(int status, string msg);
public LicenseInvalidateEventHandler LicenseInvalidateEvent;
private string rootPath = string.Empty;
public string UrlRootPath
{
get { return rootPath; }
set { rootPath = value; }
}
private string processID = string.Empty;
public string ProcessID
{
get
{
if (string.IsNullOrEmpty(processID))
{
processID = Guid.NewGuid().ToString();
}
return processID;
}
}
private readonly HttpClient _httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(1000) };
// 使用 CancellationTokenSource 来控制后台任务的启停。
private CancellationTokenSource _cts;
private readonly object _lock = new object();
// 将硬编码的URL和间隔时间定义为常量或静态变量便于修改。
private const string ServiceUrlTemplate = "requestPermission?ip={0}&userName={1}&hostName={2}&token={3}&feature={4}&version={5}";
public async Task<ReplyMessage> TestServerAsync(string serverUrl)
{
string strServicePath = $"{serverUrl}/sayHello";
HttpClient httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(20) };
try
{
string stringResult = await httpClient.GetStringAsync(strServicePath);
ReplyMessage reply = JsonConvert.DeserializeObject<ReplyMessage>(stringResult);
return reply;
}
catch (Exception ex)
{
ReplyMessage msg = new ReplyMessage();
msg.Code = 0;
msg.Message = ex.Message;
return msg;
}
}
/// <summary>
/// 启动或更新后台心跳任务
/// </summary>
/// <param name="workPath">新的工作路径</param>
/// <param name="interval">任务执行的间隔</param>
public void StartOrUpdateTask(string workPath, TimeSpan interval)
{
this.rootPath = workPath;
lock (_lock)
{
// 如果已有任务在运行,先发送取消信号并等待其结束
_cts?.Cancel();
_cts?.Dispose();
// 创建一个新的CancellationTokenSource来启动新任务
_cts = new CancellationTokenSource();
// 使用 Task.Run 启动一个长期的后台任务
Task.Run(() => RequestPermission(workPath, interval, _cts.Token), _cts.Token);
}
}
/// <summary>
/// 后台任务的主循环替代了原来的Timer
/// </summary>
private async Task RequestPermission(string workPath, TimeSpan interval, CancellationToken cancellationToken)
{
//interval = TimeSpan.FromSeconds(10);
Trace.WriteLine("后台分析任务已启动...");
while (!cancellationToken.IsCancellationRequested)
{
try
{
LicenseInfo license = Security.CurentLicenseInfo;
// --- 单次任务的核心逻辑 ---
Trace.WriteLine($"开始执行分析... WorkPath: {workPath}");
string strServicePath = $"{workPath}/{ServiceUrlTemplate}";
string requestUrl = string.Format(strServicePath,
Uri.EscapeDataString(GetActiveIPv4Address()),
Uri.EscapeDataString(Environment.UserName),
Uri.EscapeDataString(Environment.MachineName),
Uri.EscapeDataString(ProcessID),
Uri.EscapeDataString(license.Products),
Uri.EscapeDataString(license.Version));
// 使用静态的 HttpClient 实例发送请求
string stringResult = await _httpClient.GetStringAsync(requestUrl);
// 反序列化并处理结果
ReplyMessage reply = JsonConvert.DeserializeObject<ReplyMessage>(stringResult);
LicenseInvalidateEvent?.Invoke(reply.Code, reply.Message);
//Trace.WriteLine($"服务调用成功,返回: {cleanString}");
}
catch (HttpRequestException ex)
{
Trace.WriteLine($"服务调用失败(HTTP): {ex.Message}");
LicenseInvalidateEvent?.Invoke(0, ex.Message);
}
catch (TaskCanceledException ex)
{
// 检查这个取消是否由HttpClient超时引起
if (ex.CancellationToken.IsCancellationRequested == false)
{
string timeoutMessage = $"服务调用超时(超过 {_httpClient.Timeout.TotalSeconds} 秒)。";
LicenseInvalidateEvent?.Invoke(0, timeoutMessage); // 触发超时事件
}
else
{
// 如果是由外部的cancellationToken引发的则是正常停止
Trace.WriteLine("任务被外部取消。");
break; // 正常退出循环
}
break;
}
catch (Exception ex)
{
LicenseInvalidateEvent?.Invoke(2, ex.Message);
// 捕获所有其他异常,防止后台任务崩溃
Trace.WriteLine($"后台任务执行出错: {ex.Message}");
}
//// 验证许可成功
//LicenseInvalidateEvent?.Invoke(1, "验证成功!");
try
{
// 使用 Task.Delay 代替 Timer 的 period它能被安全地取消。
await Task.Delay(interval, cancellationToken);
}
catch (TaskCanceledException)
{
// 等待期间任务被取消,正常退出
break;
}
}
Trace.WriteLine("后台分析任务已优雅地停止。");
}
/// <summary>
/// 停止后台任务
/// </summary>
public void StopTask()
{
lock (_lock)
{
_cts?.Cancel(); // 发送取消信号
_cts?.Dispose();
_cts = null;
Trace.WriteLine("已发送停止信号。");
}
}
public Task SendOfflineAsync()
{
string offlineUrlTemplate = "requestOffline?token={0}";
LicenseInfo license = Security.CurentLicenseInfo;
string strServicePath = $"{this.rootPath}/{offlineUrlTemplate}";
string requestUrl = string.Format(strServicePath,
Uri.EscapeDataString(ProcessID));
// 使用静态的 HttpClient 实例发送请求
return _httpClient.GetStringAsync(requestUrl);
}
public static string GetActiveIPv4Address()
{
// 1. 获取所有网络接口
var allInterfaces = NetworkInterface.GetAllNetworkInterfaces();
// 2. 筛选出我们感兴趣的接口
var activeInterface = allInterfaces.FirstOrDefault(ni =>
// a. 接口必须是活动的 (Up)
ni.OperationalStatus == OperationalStatus.Up &&
// b. 接口不能是环回接口 (e.g., 127.0.0.1)
ni.NetworkInterfaceType != NetworkInterfaceType.Loopback &&
// c. 接口必须有网关地址 (这是关键!)
ni.GetIPProperties().GatewayAddresses.Any());
if (activeInterface == null)
{
return "未找到活动的网络接口。";
}
// 3. 从该接口的 IP 属性中找到 IPv4 地址
var ipProperties = activeInterface.GetIPProperties();
var ipv4Address = ipProperties.UnicastAddresses
.FirstOrDefault(ua => ua.Address.AddressFamily == AddressFamily.InterNetwork);
return ipv4Address?.Address.ToString() ?? string.Empty;
}
}
/// <summary>
/// 应答消息类.
/// </summary>
public class ReplyMessage
{
/// <summary>
/// Initializes a new instance of the <see cref="ReplyMessage"/> class.
/// </summary>
public ReplyMessage()
{
}
/// <summary>
/// 消息代码
/// </summary>
public int Code { get; set; } = 0;
/// <summary>
/// 消息内容
/// </summary>
public string Message { get; set; } = string.Empty;
}
}