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

els*_*nwx 5 .net c#

我想检查一个主机是否可以解析?但我不想等待很长时间;所以我想设置一个超时。

我试过了

 public static bool ResolveTest(string hostNameOrAddress, TimeSpan time_out)
 {
     var result = Dns.BeginGetHostEntry(hostNameOrAddress, null, null);
     var success = result.AsyncWaitHandle.WaitOne(time_out);
     if (!success) {
         //throw new Exception("Failed to resolve the domain.");
         return false;
     }
     return true;
 }
Run Code Online (Sandbox Code Playgroud)

但这不能正常工作,因为当它是错误的主机时,它也可以返回 true。那么如何设置 DnsResolve 的超时时间呢?

Nic*_*ico 2

原答案

查看更新的答案

没有办法实际取消,Dns.BeginGetHostEntry()但您可以尝试实现可以​​在设定时间后取消的东西。这有点骇人听闻,但可以按照您的要求进行。

然而,DNS 查找(通常)非常快,并且在测试中通常会在 10 毫秒内解析。

这是一个示例。基本上制作一个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
}
Run Code Online (Sandbox Code Playgroud)

然后可以将该方法调用为

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

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

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

同样,这有点hackish,但确实达到了预期的效果。现在这也已完成,async以便您可以使用async&await功能。

更新答案

在第一个答案之后,我不喜欢这个结果,并回顾了您最初的问题。您走在正确的轨道上,只是我们需要像您一样检查完整的标志。但是,如果调用后未完成,success将返回 false 。如果完成,那么(即使没有解析 IP 地址)您需要使用.resultWaitOnesuccesstrueDns.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;
}
Run Code Online (Sandbox Code Playgroud)

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