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

时间:2016-08-17 02:44:57

标签: c# .net

我想检查主机是否可以解析,但我不想等待很长时间,所以我想设置超时。 我试过了

 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。那么如何设置DnsResolve的超时?

3 个答案:

答案 0 :(得分:3)

原始答案

查看更新后的答案

无法实际取消Dns.BeginGetHostEntry(),但您可以尝试实施可在设定时间后取消的内容。这有点hackish但可以按照你的要求做。

然而,DNS查找(通常)非常快,并且测试通常在10ms内解决。

这是一个示例。基本上基于Task的方法允许您执行Task,无论结果是否已完成,都会返回bool。

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

执行Resolve后,它会创建一个内部AsyncCallBack委托。然后将此委托传递给Dns.BeginGetHostEntry()方法。另外,我们传入state ResolveState对象。该状态对象可用于管理上下文之间的状态。因此,当AsyncCallBack执行时,我们可以设置Result的{​​{1}}。接下来,设置ResolveState标志以指示回调委托已完成。

最后(这是我不喜欢的部分)然后我们有一个执行每completed的循环,如果超时到期,则将我们的状态对象的1ms设置为Result

这又是一种轻微的hackish,但确实会带来所需的效果。现在这也完成了Timeout,因此您可以使用async&amp; async功能。

更新了答案

在第一个回答后,我不喜欢结果,并回顾了你的初步问题。你在正确的轨道上,除了我们需要像你一样检查完整的旗帜。但是,如果在await来电后success未完成,result将返回false。如果完成,那么WaitOnesuccess(即使没有解析IP地址),您还需要使用true检查结果。

修订代码:

Dns.EndGetHostEntry

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

答案 1 :(得分:0)

作为替代解决方案,NuGet库DNS Client允许配置请求超时。

用法:

var dns = new DnsClient.LookupClient();
dns.Timeout = TimeSpan.FromSeconds(1);
dns.ThrowDnsErrors = true;

//will succeed
var dnsResult1 = dns.GetHostEntry("www.google.com");

//will throw an exception; or, if ThrowDnsErrors is false, return null
var dnsResult2 = dns.GetHostEntry("bogus");

答案 2 :(得分:-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();
}