MD5Tools/Form1.cs
Dong 52ce8400ba feat(更新窗体): 优化更新流程和窗体显示
- 添加进度条更新逻辑,确保进度显示准确
- 设置窗体位置到屏幕右下角并置顶显示
- 移除窗体边框,优化视觉效果
- 添加总文件数统计,完善下载进度显示
- 优化状态更新逻辑,确保UI实时刷新
2025-05-07 13:58:32 +08:00

862 lines
34 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace CheckDownload
{
/// <summary>
/// 自动更新窗体类,负责检查和下载软件更新
/// </summary>
public partial class Update : Form
{
/// <summary>本地MD5文件名</summary>
private const string LocalMd5File = "md5.json";
/// <summary>DNS查询域名用于获取更新信息</summary>
private const string DnsQueryDomain = "test.file.ipoi.cn";
/// <summary>基础下载URL</summary>
private const string BaseDownloadUrl = "http://localhost:60006/";
/// <summary>最大并发下载数</summary>
private const int MaxConcurrentDownloads = 5;
/// <summary>在线MD5文件名从DNS查询获取</summary>
private string _onlineMd5File = "";
// 类级别变量,用于存储下载的文件数据
private Dictionary<string, (byte[] Data, string ExpectedMd5)> _downloadedFiles = new Dictionary<string, (byte[], string)>();
/// <summary>已完成下载的文件数</summary>
private int _completedFiles = 0;
/// <summary>需要下载的总文件数</summary>
private int _totalFilesToDownload = 0;
/// <summary>
/// 构造函数,初始化更新窗体
/// </summary>
public Update()
{
InitializeComponent();
ConfigureProgressBar();
}
/// <summary>
/// 配置进度条初始设置
/// </summary>
private void ConfigureProgressBar()
{
Update_Pro.Minimum = 0; // 设置最小值
Update_Pro.Maximum = 100; // 设置最大值
Update_Pro.Value = 0; // 设置初始值
Update_Pro.Step = 1; // 设置步进值
}
/// <summary>
/// 更新进度条值
/// </summary>
/// <param name="percentage">进度百分比(0-100)</param>
private void UpdateProgressValue(int percentage)
{
// 确保百分比在有效范围内
if (percentage < 0) percentage = 0;
if (percentage > 100) percentage = 100;
// 在UI线程上更新进度条值
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
// 确保进度条值不超出范围
if (percentage >= Update_Pro.Minimum && percentage <= Update_Pro.Maximum)
{
Update_Pro.Value = percentage;
}
else
{
// 如果超出范围,则设置为最大或最小值
Update_Pro.Value = Math.Min(Math.Max(percentage, Update_Pro.Minimum), Update_Pro.Maximum);
}
// 添加Application.DoEvents()调用,确保消息队列被处理
Application.DoEvents();
});
}
else
{
if (percentage >= Update_Pro.Minimum && percentage <= Update_Pro.Maximum)
{
Update_Pro.Value = percentage;
}
else
{
Update_Pro.Value = Math.Min(Math.Max(percentage, Update_Pro.Minimum), Update_Pro.Maximum);
}
Application.DoEvents();
}
}
/// <summary>
/// 窗体加载事件处理方法,启动更新流程
/// </summary>
/// <param name="sender">事件源</param>
/// <param name="e">事件参数</param>
public async void Update_Load(object sender, EventArgs e)
{
// 设置窗体位置到屏幕右下角
PositionFormToBottomRight();
await UpdateFile(); // 执行更新流程
// 注意窗体关闭逻辑已移至UpdateFile方法中确保显示完成消息后再关闭
}
/// <summary>
/// 设置窗体位置到屏幕右下角
/// </summary>
private void PositionFormToBottomRight()
{
// 获取当前屏幕的工作区域(排除任务栏等系统元素)
Rectangle workingArea = Screen.GetWorkingArea(this);
// 计算窗体在右下角的位置
int x = workingArea.Right - this.Width;
int y = workingArea.Bottom - this.Height;
// 设置窗体位置
this.Location = new Point(x, y);
}
/// <summary>
/// 主要更新流程,检查并下载更新文件
/// </summary>
/// <returns>更新任务</returns>
private async Task UpdateFile()
{
try
{
// 定义更新流程的总步骤数和当前步骤
const int totalSteps = 6; // 总共6个主要步骤
int currentStep = 0;
// 更新进度条初始状态
UpdateProgressValue(0);
// 步骤1: 读取本地MD5文件信息
UpdateStatus("读取本地MD5文件...");
var localData = await ReadLocalMd5File();
if (!ValidateLocalData(localData.Version, localData.Md5, localData.Data)) return;
UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为16%
// 步骤2: 获取在线MD5文件信息
UpdateStatus("获取在线MD5文件信息...");
var onlineData = await GetOnlineMd5File();
if (!ValidateOnlineData(onlineData.Version, onlineData.Md5)) return;
UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为33%
// 步骤3: 比较版本,判断是否需要更新
UpdateStatus("比较版本信息...");
if (!ShouldUpdate(localData.Version, onlineData.Version))
{
UpdateStatus("当前已是最新版本,无需更新");
UpdateProgressValue(100); // 完成进度
return;
}
UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为50%
// 步骤4: 下载在线MD5文件
UpdateStatus("下载在线MD5文件...");
var onlineFileData = await DownloadOnlineMd5File();
if (onlineFileData == null) return;
UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为66%
// 步骤5: 比较文件差异
UpdateStatus("比较文件差异...");
var differences = CompareDataDifferences(localData.Data, onlineFileData);
UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为83%
// 步骤6: 下载和更新文件
if (differences.Count > 0)
{
// 下载需要更新的文件
await DownloadUpdatedFiles(differences);
}
else
{
UpdateStatus("无文件需要更新");
}
// 替换本地MD5文件
ReplaceLocalMd5File();
UpdateProgressValue(100); // 完成进度
// 显示更新完成消息
UpdateStatus("更新完成");
// 等待1秒后关闭窗体
await Task.Delay(1000);
this.Close();
}
catch (Exception ex)
{
UpdateStatus($"发生错误: {ex.Message}");
}
}
/// <summary>
/// 验证本地数据的有效性
/// </summary>
/// <param name="version">版本号</param>
/// <param name="md5">MD5校验值</param>
/// <param name="data">文件数据对象</param>
/// <returns>数据是否有效</returns>
private bool ValidateLocalData(string version, string md5, JObject data)
{
if (string.IsNullOrEmpty(version) || string.IsNullOrEmpty(md5) || data == null)
{
UpdateStatus("本地MD5文件无效");
return false;
}
return true;
}
/// <summary>
/// 验证在线数据的有效性
/// </summary>
/// <param name="version">版本号</param>
/// <param name="md5">MD5校验值</param>
/// <returns>数据是否有效</returns>
private bool ValidateOnlineData(string version, string md5)
{
if (string.IsNullOrEmpty(version) || string.IsNullOrEmpty(md5))
{
UpdateStatus("无法获取在线MD5信息");
return false;
}
return true;
}
/// <summary>
/// 读取本地MD5文件并解析内容
/// </summary>
/// <returns>包含版本号、MD5值和数据对象的元组</returns>
private async Task<(string Version, string Md5, JObject Data)> ReadLocalMd5File()
{
try
{
UpdateStatus("读取md5.json...");
// 异步读取文件内容
string json = await Task.Run(() => File.ReadAllText(LocalMd5File));
// 解析JSON对象
var obj = JObject.Parse(json);
// 获取版本号
string version = obj["version"]?.ToString();
// 获取数据对象
var data = (JObject)obj["data"];
// 计算JSON内容的MD5值
string jsonMd5 = CalculateMD5(json);
return (version, jsonMd5, data);
}
catch (Exception ex) when (ex is FileNotFoundException || ex is JsonException)
{
UpdateStatus($"读取本地MD5文件失败: {ex.Message}");
return (null, null, null);
}
}
/// <summary>
/// 获取在线MD5文件信息
/// </summary>
/// <returns>包含版本号和MD5值的元组</returns>
private async Task<(string Version, string Md5)> GetOnlineMd5File()
{
try
{
UpdateStatus("解析在线md5.json...");
// 通过DNS查询获取响应数据
string responseData = await QueryDnsAsync();
// 反序列化JSON字符串
string firstUnescaped = JsonConvert.DeserializeObject<string>(responseData);
// 解析JSON对象
var dataJson = JObject.Parse(firstUnescaped);
// 获取版本号和MD5值
string version = dataJson["version"]?.ToString();
string md5 = dataJson["md5"]?.ToString();
// 设置在线MD5文件名
_onlineMd5File = $"{md5}.json";
return (version, md5);
}
catch (Exception ex)
{
UpdateStatus($"获取在线MD5信息失败: {ex.Message}");
return (null, null);
}
}
/// <summary>
/// 判断是否需要更新
/// </summary>
/// <param name="localVersion">本地版本号</param>
/// <param name="onlineVersion">在线版本号</param>
/// <returns>是否需要更新</returns>
private bool ShouldUpdate(string localVersion, string onlineVersion)
{
try
{
UpdateStatus("校验信息...");
// 解析版本号字符串为Version对象
var localVer = new Version(localVersion);
var onlineVer = new Version(onlineVersion);
// 比较版本号,如果本地版本小于在线版本,则需要更新
return localVer.CompareTo(onlineVer) < 0;
}
catch (Exception ex) when (ex is FormatException || ex is ArgumentNullException)
{
UpdateStatus($"版本比较失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 下载在线MD5文件并解析内容
/// </summary>
/// <returns>文件数据对象</returns>
private async Task<JObject> DownloadOnlineMd5File()
{
try
{
UpdateStatus("下载在线md5.json文件...");
// 使用WebClient下载文件
using (var client = new WebClient())
{
await client.DownloadFileTaskAsync(
new Uri($"{BaseDownloadUrl}{_onlineMd5File}"),
_onlineMd5File);
}
UpdateStatus("读取在线md5.json文件...");
// 异步读取文件内容
string json = await Task.Run(() => File.ReadAllText(_onlineMd5File));
// 解析JSON对象
var obj = JObject.Parse(json);
// 返回数据部分
return (JObject)obj["data"];
}
catch (Exception ex)
{
UpdateStatus($"下载在线MD5文件失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 比较本地数据和在线数据的差异
/// </summary>
/// <param name="localData">本地数据对象</param>
/// <param name="onlineData">在线数据对象</param>
/// <param name="currentPath">当前路径,用于递归调用</param>
/// <returns>需要更新的文件路径和MD5值的字典</returns>
private Dictionary<string, string> CompareDataDifferences(JObject localData, JObject onlineData, string currentPath = "")
{
var differences = new Dictionary<string, string>();
foreach (var onlineProperty in onlineData.Properties())
{
// 获取属性名称
string key = onlineProperty.Name;
// 获取属性值
JToken onlineValue = onlineProperty.Value;
// 构建完整路径
string fullPath = string.IsNullOrEmpty(currentPath) ? key : $"{currentPath}/{key}";
if (onlineValue.Type == JTokenType.String)
{
// 如果是字符串类型表示是文件的MD5值
string expectedMd5 = onlineValue.ToString();
// 判断是否需要下载该文件
if (ShouldDownloadFile(localData, key, expectedMd5, fullPath))
{
differences[fullPath] = expectedMd5;
}
}
else if (onlineValue.Type == JTokenType.Object)
{
// 如果是对象类型,表示是目录,递归比较
JObject localSubData = GetSubData(localData, key);
var subDifferences = CompareDataDifferences(localSubData, (JObject)onlineValue, fullPath);
foreach (var diff in subDifferences)
{
differences[diff.Key] = diff.Value;
}
}
}
return differences;
}
/// <summary>
/// 判断是否需要下载文件
/// </summary>
/// <param name="localData">本地数据对象</param>
/// <param name="key">文件键名</param>
/// <param name="expectedMd5">期望的MD5值</param>
/// <param name="fullPath">文件完整路径</param>
/// <returns>是否需要下载</returns>
private bool ShouldDownloadFile(JObject localData, string key, string expectedMd5, string fullPath)
{
// 本地数据中不存在该键
bool fileMissing = !localData.ContainsKey(key);
// 本地数据中存在该键但MD5值不匹配
bool md5Mismatch = localData.ContainsKey(key) &&
(localData[key].Type != JTokenType.String ||
localData[key].ToString() != expectedMd5);
// 物理文件不存在
bool physicalFileMissing = !File.Exists(Path.Combine(".", fullPath.Replace('/', '\\')));
// 满足任一条件则需要下载
return fileMissing || md5Mismatch || physicalFileMissing;
}
/// <summary>
/// 获取本地数据中的子数据对象
/// </summary>
/// <param name="localData">本地数据对象</param>
/// <param name="key">子数据键名</param>
/// <returns>子数据对象,如果不存在则返回空对象</returns>
private JObject GetSubData(JObject localData, string key)
{
// 如果本地数据中存在该键且类型为对象,则返回该对象,否则返回空对象
return localData.ContainsKey(key) && localData[key].Type == JTokenType.Object
? (JObject)localData[key]
: new JObject();
}
/// <summary>
/// 下载需要更新的文件
/// </summary>
/// <param name="differences">需要更新的文件路径和MD5值的字典</param>
/// <returns>下载任务</returns>
private async Task DownloadUpdatedFiles(Dictionary<string, string> differences)
{
// 重置下载状态
_completedFiles = 0;
_downloadedFiles.Clear();
// 设置总文件数
_totalFilesToDownload = differences.Count;
// 更新状态信息
UpdateStatus($"开始下载更新文件,共{differences.Count}个文件...");
// 创建信号量控制并发下载数量
var semaphore = new SemaphoreSlim(MaxConcurrentDownloads);
var downloadTasks = new List<Task>();
// 为每个需要更新的文件创建下载任务
foreach (var file in differences)
{
downloadTasks.Add(DownloadFileWithSemaphore(file, semaphore));
}
// 等待所有下载任务完成
await Task.WhenAll(downloadTasks);
// 所有文件下载完成后统一校验和保存
UpdateStatus("所有文件下载完成,开始校验和保存...");
VerifyAndSaveAllFiles();
}
/// <summary>
/// 使用信号量控制并发下载文件
/// </summary>
/// <param name="file">文件路径和MD5值的键值对</param>
/// <param name="semaphore">控制并发的信号量</param>
/// <returns>下载任务</returns>
private async Task DownloadFileWithSemaphore(KeyValuePair<string, string> file, SemaphoreSlim semaphore)
{
await semaphore.WaitAsync();
try
{
await DownloadToMemory(file.Key, file.Value);
}
finally
{
semaphore.Release();
}
}
/// <summary>
/// 下载文件到内存并暂存
/// </summary>
private async Task DownloadToMemory(string relativePath, string expectedMd5)
{
try
{
// 更新状态显示当前下载进度
int current = Interlocked.Increment(ref _completedFiles);
// 下载文件到内存
using (var client = new HttpClient())
{
var fileUrl = $"{BaseDownloadUrl}File/{expectedMd5}";
var response = await client.GetAsync(fileUrl);
response.EnsureSuccessStatusCode();
byte[] fileData = await response.Content.ReadAsByteArrayAsync();
// 存储到内存字典
lock (_downloadedFiles)
{
_downloadedFiles[relativePath] = (fileData, expectedMd5);
}
}
// 更新进度
UpdateProgress();
}
catch (Exception ex)
{
UpdateStatus($"下载失败: {relativePath} - {ex.Message}");
throw;
}
}
/// <summary>
/// 校验并保存所有已下载的文件
/// </summary>
private void VerifyAndSaveAllFiles()
{
UpdateStatus("正在校验文件...");
// 创建失败文件列表
var failedFiles = new List<string>();
int processedCount = 0;
int totalFiles = _downloadedFiles.Count;
foreach (var item in _downloadedFiles)
{
string relativePath = item.Key;
byte[] data = item.Value.Data;
string expectedMd5 = item.Value.ExpectedMd5;
try
{
// 更新状态信息
processedCount++;
UpdateStatus($"正在校验和保存文件 ({processedCount}/{totalFiles}): {relativePath}");
// 计算内存中数据的MD5
string actualMd5;
using (var md5 = MD5.Create())
{
byte[] hashBytes = md5.ComputeHash(data);
actualMd5 = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
}
// 校验MD5
if (actualMd5 != expectedMd5.ToLower())
{
throw new Exception($"MD5校验失败 (期望: {expectedMd5}, 实际: {actualMd5})");
}
// 获取本地路径并创建目录
string localPath = GetLocalPath(relativePath);
EnsureDirectoryExists(localPath);
// 保存文件
File.WriteAllBytes(localPath, data);
// 更新进度条 - 从95%到98%
// 确保即使在totalFiles为0的情况下也不会出现除零错误并且百分比不会超过98
int percentage = 95;
if (totalFiles > 0)
{
// 计算进度时确保不会超过98%
percentage = 95 + Math.Min(3, (int)(processedCount * 3.0 / totalFiles));
}
UpdateProgressValue(percentage);
}
catch (Exception ex)
{
UpdateStatus($"文件校验失败: {relativePath} - {ex.Message}");
failedFiles.Add(relativePath);
}
}
// 清理失败的文件记录
foreach (var failedFile in failedFiles)
{
_downloadedFiles.Remove(failedFile);
}
if (failedFiles.Count > 0)
{
throw new Exception($"{failedFiles.Count}个文件校验失败");
}
else
{
UpdateStatus("所有文件校验和保存成功");
}
}
/// <summary>
/// 获取文件的本地路径
/// </summary>
/// <param name="relativePath">文件的相对路径</param>
/// <returns>本地文件路径</returns>
private string GetLocalPath(string relativePath)
{
// 将相对路径转换为本地路径,替换斜杠为反斜杠
return Path.Combine(".", relativePath.Replace('/', '\\'));
}
/// <summary>
/// 确保文件所在目录存在
/// </summary>
/// <param name="filePath">文件路径</param>
private void EnsureDirectoryExists(string filePath)
{
// 获取文件所在目录
string directory = Path.GetDirectoryName(filePath);
// 如果目录不存在,则创建目录
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
}
/// <summary>
/// 验证文件的MD5值
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="expectedMd5">期望的MD5值</param>
/// <param name="relativePath">文件的相对路径,用于错误提示</param>
/// <exception cref="Exception">当MD5校验失败时抛出异常</exception>
private void VerifyFileMd5(string filePath, string expectedMd5, string relativePath)
{
// 计算文件的实际MD5值
string actualMd5 = CalculateFileMD5(filePath);
// 如果MD5值不匹配删除文件并抛出异常
if (actualMd5 != expectedMd5)
{
File.Delete(filePath);
throw new Exception($"MD5校验失败: {relativePath}");
}
}
/// <summary>
/// 替换现有文件
/// </summary>
/// <param name="tempPath">临时文件路径</param>
/// <param name="localPath">目标文件路径</param>
private void ReplaceExistingFile(string tempPath, string localPath)
{
// 如果目标文件已存在,则先删除
if (File.Exists(localPath))
{
File.Delete(localPath);
}
// 将临时文件移动到目标位置
File.Move(tempPath, localPath);
}
/// <summary>
/// 更新文件下载进度
/// </summary>
private void UpdateProgress()
{
// 计算下载进度百分比,避免除零错误
int percentage = 0;
if (_totalFilesToDownload > 0)
{
percentage = (int)(_completedFiles * 100.0 / _totalFilesToDownload);
}
// 更新状态信息
UpdateStatus($"正在下载文件 ({_completedFiles}/{_totalFilesToDownload})...");
// 在UI线程上更新进度条值
this.BeginInvoke((MethodInvoker)delegate
{
// 在下载阶段进度从83%到95%之间变化
// 确保不会超过95%
int overallPercentage = 83 + Math.Min(12, (int)(percentage * 12.0 / 100));
// 使用UpdateProgressValue方法确保值在有效范围内
UpdateProgressValue(overallPercentage);
});
}
/// <summary>
/// 替换本地MD5文件为在线下载的新版本
/// </summary>
private void ReplaceLocalMd5File()
{
try
{
// 如果本地MD5文件已存在则先删除
if (File.Exists(LocalMd5File))
{
File.Delete(LocalMd5File);
}
// 将下载的在线MD5文件移动到本地MD5文件位置
File.Move(_onlineMd5File, LocalMd5File);
UpdateStatus("更新完成");
}
catch (Exception ex)
{
UpdateStatus($"更新本地版本信息失败: {ex.Message}");
}
}
/// <summary>
/// 更新状态信息显示
/// </summary>
/// <param name="message">要显示的状态信息</param>
private void UpdateStatus(string message)
{
// 使用Invoke而不是BeginInvoke确保UI更新完成
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
Status_Box.Text = message;
// 立即刷新控件,确保状态实时显示
Status_Box.Update();
// 添加Application.DoEvents()调用,确保消息队列被处理
Application.DoEvents();
});
}
else
{
Status_Box.Text = message;
Status_Box.Update();
Application.DoEvents();
}
}
/// <summary>
/// 计算字符串的MD5值
/// </summary>
/// <param name="input">输入字符串</param>
/// <returns>MD5哈希值小写十六进制字符串</returns>
private string CalculateMD5(string input)
{
using (var md5 = MD5.Create())
{
// 将字符串转换为字节数组
byte[] inputBytes = Encoding.UTF8.GetBytes(input);
// 计算哈希值
byte[] hashBytes = md5.ComputeHash(inputBytes);
// 将字节数组转换为十六进制字符串并返回
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
}
}
/// <summary>
/// 计算文件的MD5值
/// </summary>
/// <param name="filePath">文件路径</param>
/// <returns>MD5哈希值小写十六进制字符串</returns>
private string CalculateFileMD5(string filePath)
{
using (var md5 = MD5.Create())
using (var stream = File.OpenRead(filePath))
{
// 计算文件流的哈希值
byte[] hashBytes = md5.ComputeHash(stream);
// 将字节数组转换为十六进制字符串并返回
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
}
}
/// <summary>
/// 通过多个DNS over HTTPS服务器查询TXT记录
/// </summary>
/// <returns>查询到的TXT记录内容</returns>
private static async Task<string> QueryDnsAsync()
{
// DNS over HTTPS服务器列表
var dohServers = new List<string>
{
"https://cloudflare-dns.com/dns-query",
"https://dns.cloudflare.com/dns-query",
"https://1.1.1.1/dns-query",
"https://1.0.0.1/dns-query",
"https://dns.google/resolve",
"https://sm2.doh.pub/dns-query",
"https://doh.pub/dns-query",
"https://dns.alidns.com/resolve",
"https://223.5.5.5/resolve",
"https://223.6.6.6/resolve",
"https://doh.360.cn/resolve"
};
using (var httpClient = new HttpClient())
{
// 创建取消令牌源,用于取消其他任务
var cts = new CancellationTokenSource();
var tasks = new List<Task<string>>();
// 并行查询所有DNS服务器
foreach (var server in dohServers)
{
tasks.Add(QueryDnsServer(httpClient, server, cts.Token));
}
// 等待任意一个任务完成
var completedTask = await Task.WhenAny(tasks);
// 取消其他正在进行的任务
cts.Cancel();
// 返回第一个完成的任务结果
return await completedTask;
}
}
/// <summary>
/// 查询指定DNS over HTTPS服务器的TXT记录
/// </summary>
/// <param name="client">HTTP客户端</param>
/// <param name="server">DNS服务器URL</param>
/// <param name="token">取消令牌</param>
/// <returns>查询到的TXT记录内容失败则返回空字符串</returns>
private static async Task<string> QueryDnsServer(HttpClient client, string server, CancellationToken token)
{
try
{
// 构建DNS查询URL
var url = $"{server}?name={DnsQueryDomain}&type=TXT";
// 发送HTTP请求获取响应
var response = await client.GetStringAsync(url);
// 解析JSON响应
var jsonResponse = JObject.Parse(response);
// 遍历Answer部分查找TXT记录
foreach (var record in jsonResponse["Answer"])
{
string txtRecord = record["data"]?.ToString();
// 如果找到有效的TXT记录则返回
if (!string.IsNullOrEmpty(txtRecord))
{
return txtRecord;
}
}
return string.Empty;
}
catch
{
// 查询失败时返回空字符串
return string.Empty;
}
}
}
}