筆者近期作的UWP項目須要實現的一個小的功能模塊--對指定Dns服務器發起域名解析的請求。剛開始的時候遇到了不少坑,翻閱了dalao們的文章,回答,查閱了官方文檔,最終找到了合適的Nuget包得以解決,特此摘記下這個過程:git
先是UWP System.Net命名空間下暫時不能作到對指定的Dns服務器發起這樣的請求(System.Net.Dns 僅支持用戶使用本地選擇的Dns服務器進行域名解析)。上一段在StackOverFlow上dalao關於Dns類的描述:github
No this cannot be done with the .Net Framework. The Dns.Resolve method relies on the internal Win32 APIs which in turn go through the DNS servers associated with the network connection.c#
In order to get this to work, you'd have to change the DNS servers associated with the network adapter's address.windows
OK,而後筆者嘗試了在UWP中調用cmd進行nslookup查詢(nslookup 是用於查詢DNS的記錄的命令)。可是!可是!可是,UWP說明是沒法直接調用一系列腳本文件(包括.exe),上一段StackOverFlow裏dalao的回覆:api
You cannot not launch external executable from your UWP application. This is prevented by the security model. You are restricted to the methods provided by the Launcher API.服務器
You can open a file with its default application using LaunchFile or LaunchUri. The system will start the application registered by the user to open the file.app
而後筆者就去查閱了Launcher類的使用,發現了在Launcher.LaunchFile()方法的描述中,官方有一段特地的強調:dom
The calling app must be visible to the user when the API is invoked.async
This API must be called from an ASTA thread (also known as a UI thread).ide
This API also imposes several restrictions on what types of files it can launch. Many file types that contain executable code, for example .exe, .msi, and .js files, are blocked from launching. This restriction protects users from potentially malicious files that could modify the system.
OK,而後筆者查找了不少REST API也沒有找到特別合適的,Nuget包裏有不少也用不了,最後在一位dalao的回覆中找到了這個:Heijden.Dns.Portable --Nuget 包的github傳送門
一個dalao作的Dns Nuget包,支持指定Dns服務器!
OK,話很少說,上段代碼:
using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ServerMonitor.Controls { public class DnsRequest : BasicRequest { // 繼承的屬性:CreateTime TimeCost OverTime Status Others ErrorException /// <summary> /// Dns解析記錄類型,默認是A記錄 /// </summary> QType recordType = QType.A; /// <summary> /// 測試服務器狀態使用的域名 /// </summary> string domainName; /// <summary> /// 測試期待值 /// </summary> HashSet<string> actualResult = null; /// <summary> /// Dns服務器IP地址 /// </summary> IPAddress dnsServer; public QType RecordType { get => recordType; set => recordType = value; } public string DomainName { get => domainName; set => domainName = value; } public IPAddress DnsServer { get => dnsServer; set => dnsServer = value; } public HashSet<string> ActualResult { get => actualResult; set => actualResult = value; } /// <summary> /// 生成一個Dns請求對象 /// </summary> /// <param name="DnsServer">用於解析域名的Dns服務器</param> /// <param name="DomainName">待解析的域名</param> public DnsRequest(IPAddress DnsServer, string DomainName) { this.DnsServer = DnsServer; this.DomainName = DomainName; } /// <summary> /// Dns請求 /// </summary> /// <returns></returns> public override async Task<bool> MakeRequest() { // 賦值生成請求的時間 CreateTime = DateTime.Now; // 建立解析使用的Dns服務器 var resolver = new Resolver(DnsServer, 53); Response response = null; // 超時控制 CancellationTokenSource cts = new CancellationTokenSource(); cts.CancelAfter(OverTime); // 這裏二次封裝是爲了引入超時控制 try { // 記錄請求耗時 Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); // 二次封裝任務 Task t = Task.Run(async () => { response = await resolver.Query(DomainName, RecordType).ConfigureAwait(false); }, cts.Token); await t; stopwatch.Stop(); if (response.Answers.Count != 0&&t.IsCompleted) // 請求成功,獲取到了解析結果 { // Dns服務器狀態良好 Status = "1000"; // 請求耗時應該在2^15-1(ms)內完成 TimeCost = (short)stopwatch.ElapsedMilliseconds; // 記錄解析記錄 actualResult = new HashSet<string>(); foreach (var item in response.Answers) { actualResult.Add(item.RECORD.ToString()); } Debug.WriteLine(TimeCost+"eeeeeeeeeeeee"); return true; } else // 請求失敗,無解析結果 { // Dns服務器狀態未知,可是該域名沒法解析 Status = "1002"; // 請求耗時應該在2^15-1(ms)內完成 TimeCost = (short)stopwatch.ElapsedMilliseconds; ActualResult.Add("No Data!"); return false; } } // 捕獲到請求超時的狀況 catch (TaskCanceledException e) { // Dns服務器超時 Status = "1003"; // 收集捕獲到的異常 ErrorException = e; // 請求耗時設置爲超時上限 TimeCost = OverTime; return false; } // 這個是TaskCanceledException的基類 catch (OperationCanceledException e) { // Dns服務器超時 Status = "1003"; // 收集捕獲到的異常 ErrorException = e; // 請求耗時設置爲超時上限 TimeCost = OverTime; return false; } // 用於後期作異常捕獲延伸 catch (Exception e) { // Dns服務器請求出現未捕獲到的異常 Status = "1002"; // 收集捕獲到的異常 ErrorException = e.InnerException; // 請求耗時設置爲超時上限 TimeCost = OverTime; return false; } } /// <summary> /// /// </summary> /// <param name="Domainname"></param> /// <returns></returns> private bool IsDomainnameCorrect(string Domainname) { if (string.IsNullOrEmpty(Domainname)) { return false; } else { // 判斷域名是否合法 ,待補充... return Uri.IsWellFormedUriString(Domainname, UriKind.Absolute); } } /// <summary> /// 檢查expectResult是否命中解析結果resultSet /// </summary> /// <param name="expectResult"></param> /// <param name="resultSet"></param> /// <returns></returns> public bool IsMatchResult(string expectResult,HashSet<string> resultSet) { return resultSet.Contains(expectResult); } /** * int i = 0; * QType q = (QType)Enum.Parse(typeof(QType), i.ToString()); * 用來獲取枚舉值得下標 */ } }
PS:recordType 是有不少種的,取決於Dns服務器登記上關於此域名的記錄是哪一種類型的(一般多數採用的:A、CNAME、SOA、MX等)
OK,先記錄到這!