C#:如何设置DNS解析超时?

问题描述:

我想检查主机是否可以解决,但我不想等很长时间,所以我想设置超时。 我试图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

原来的答案

看看更新的答案

没有实际上取消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

目前公认的答案是很旧的和复杂的,现在你可以很容易地使用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(); 
}