|
|
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;
|
|
|
}
|
|
|
}
|