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

最佳答案 原始答案

看看更新的答案

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

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

这是一个例子.基本上基于任务的方法允许您执行一个返回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()方法.另外,我们传入ResolveState的状态对象.该状态对象可用于管理上下文之间的状态.因此,当执行AsyncCallBack时,我们可以设置ResolveState的Result.接下来,设置completed标志以指示回调委托已完成.

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

这又是一种轻微的hackish,但确实产生了预期的效果.现在这也是异步,所以你可以使用async&等待功能.

更新的答案

在第一个答案后,我不喜欢结果,并审查了您的初始问题.你在正确的轨道上,除了我们需要像你一样检查完整的旗帜.但是,如果在WaitOne调用之后结果未完成,则成功将返回false.如果完成则成功将成立(即使没有解析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.

点赞