2016-08-17 54 views
4

我想檢查主機是否可以解決,但我不想等很長時間,所以我想設置超時。 我試圖C#:如何設置DNS解析超時?

public static bool ResolveTest(string hostNameOrAddress, int millisecond_time_out) 
{ 
    var timeoutSpan = TimeSpan.FromMilliseconds(millisecond_time_out); 
    var result = Dns.BeginGetHostEntry(hostNameOrAddress, null, null); 
    var success = result.AsyncWaitHandle.WaitOne(timeoutSpan); 
    if (!success) { 
     //throw new Exception("Failed to resolve the domain."); 
     return false; 
    } 
    return true; 
} 

但現在工作這麼正確,當它是一個錯誤的主機,它也可以返回true.So如何設置DnsResolve超時?

+0

答案已更新,更好看的版本 – Nico

回答

2

原來的答案

看看更新的答案

沒有實際上取消Dns.BeginGetHostEntry()但你可以嘗試實現的東西,可以在設定的時間後,被取消的方式。這有些黑客,但可以按照你的要求去做。

但是,DNS查找(通常)非常快,測試通常在10ms內解析。

下面是一個示例。基本上使基於Task的方法,允許您執行一個Task返回一個布爾結果是否完成。當執行解析它創建了一個內部AsyncCallBack代表

public static Task<bool> Resolve(string hostNameOrAddress, int millisecond_time_out) 
{ 
    return Task.Run(async() => 
    { 
     bool completed = false; 
     var asCallBack = new AsyncCallback(ar => 
     { 
      ResolveState context = (ResolveState)ar.AsyncState; 
      if (context.Result == ResolveType.Pending) 
      { 
       try 
       { 
        var ipList = Dns.EndGetHostEntry(ar); 
        if (ipList == null || ipList.AddressList == null || ipList.AddressList.Length == 0) 
         context.Result = ResolveType.InvalidHost; 
        else 
         context.Result = ResolveType.Completed; 
       } 
       catch 
       { 
        context.Result = ResolveType.InvalidHost; 
       } 
      } 
      completed = true; 
     }); 
     ResolveState ioContext = new ResolveState(hostNameOrAddress); 
     var result = Dns.BeginGetHostEntry(ioContext.HostName, asCallBack, ioContext); 
     int miliCount = 0; 
     while (!completed) 
     { 
      miliCount++; 
      if (miliCount >= millisecond_time_out) 
      { 
       result.AsyncWaitHandle.Close(); 
       result = null; 
       ioContext.Result = ResolveType.Timeout; 
       break; 
      } 
      await Task.Delay(1); 
     } 
     Console.WriteLine($"The result of {ioContext.HostName} is {ioContext.Result}"); 
     return ioContext.Result == ResolveType.Completed; 
    }); 
} 

public class ResolveState 
{ 
    public ResolveState(string hostName) 
    { 
     if (string.IsNullOrWhiteSpace(hostName)) 
      throw new ArgumentNullException(nameof(hostName)); 
     _hostName = hostName; 
    } 

    readonly string _hostName; 

    public ResolveType Result { get; set; } = ResolveType.Pending; 

    public string HostName => _hostName; 

} 

public enum ResolveType 
{ 
    Pending, 
    Completed, 
    InvalidHost, 
    Timeout 
} 

的方法,然後可以稱爲

public static async void RunTest() 
{ 
    await Resolve("asdfasdfasdfasdfasdfa.ca", 60); 
    await Resolve("google.com", 60); 
} 

。然後將該代表傳遞給Dns.BeginGetHostEntry()方法。另外我們還通過了state對象ResolveState。此狀態對象可用於管理上下文之間的狀態。因此,當AsyncCallBack得到執行時,我們可以設置ResolveStateResult。接下來,completed標誌被設置爲指示回調委託完成。

最後(這是我不喜歡的部分),然後我們有一個循環,每執行和1ms如果超時設置我們的狀態對象的ResultTimeout

這又有點駭人聽聞,但確實導致了所需的效果。現在這也是async,所以你可以使用功能async & await功能。

更新回答

我不喜歡的結果,並審查了最初的問題的第一個答案之後。除了我們需要檢查完整的標誌之外,你們走在了正確的軌道上。但如果resultWaitOne調用後未完成,則success將返回false。如果完成,那麼success將是true(即使沒有解析IP地址),然後您需要使用Dns.EndGetHostEntry檢查結果。

修改後的代碼:

public static bool ResolveTest(string hostNameOrAddress, int millisecond_time_out) 
{ 
    ResolveState ioContext = new ResolveState(hostNameOrAddress); 
    var result = Dns.BeginGetHostEntry(ioContext.HostName, null, null); 
    var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(millisecond_time_out), true); 
    if (!success) 
    { 
     ioContext.Result = ResolveType.Timeout; 
    } 
    else 
    { 
     try 
     { 
      var ipList = Dns.EndGetHostEntry(result); 
      if (ipList == null || ipList.AddressList == null || ipList.AddressList.Length == 0) 
       ioContext.Result = ResolveType.InvalidHost; 
      else 
       ioContext.Result = ResolveType.Completed; 
     } 
     catch 
     { 
      ioContext.Result = ResolveType.InvalidHost; 
     } 
    } 
    Console.WriteLine($"The result of ResolveTest for {ioContext.HostName} is {ioContext.Result}"); 
    return ioContext.Result == ResolveType.Completed; 
} 

下面是完整的要點https://gist.github.com/Nico-VanHaaster/35342c1386de752674d0c37ceaa65e00

-1

目前公認的答案是很舊的和複雜的,現在你可以很容易地使用System.Threading.Tasks.Task.Run()對於這一點,是這樣的:

private Task<IPAddress> ResolveAsync(string hostName) { 
    return System.Threading.Tasks.Task.Run(() => { 
     return System.Net.Dns.GetHostEntry(hostName).AddressList[0]; 
    }); 
} 

private string ResolveWithTimeout(string hostNameOrIpAddress) { 
    var timeout = TimeSpan.FromSeconds(3.0); 
    var task = ResolveAsync(hostNameOrIpAddress); 
    if (!task.Wait(timeout)) { 
     throw new TimeoutException(); 
    } 
    return task.Result.ToString(); 
}