6

AFAIK在 OS X(和 iOS)上CFHost提供唯一的公共 API,用于异步和/或可以取消的 DNS 解析(因此可以实现自定义超时)。所有其他 API 都是同步的,无法取消,因此每次 DNS 查找都必须浪费一个线程以使操作异步或可停止(即使 Grand Central Dispatch 每次查找都会浪费一个线程,您不必创建线程你自己)。每个 DNS 解析调用都有一个阻塞线程(这样的调用可能会阻塞相当长的时间,在我的系统上,超时是 30 秒,然后调用最终会超时)如果您需要解析大量 DNS,这确实不是要走的路主机名。

CFHost对我来说似乎是一个很好的工作。它可以同步使用,在这种情况下,文档说可以从另一个线程取消阻塞请求,并且可以异步使用,在这种情况下请求在后台运行,如果需要也可以取消,但它在成功或自然超时之前不会阻塞任何线程。内部CFHost使用getaddrinfo_async_*函数,但这不是公共 API,据我所知,这些函数是私有的,不应直接使用它们。

所以这是我编写的一段简单的代码,用于CFHost使用取消测试查找,但它没有按预期工作,我不知道为什么。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <dispatch/dispatch.h>
#include <CoreServices/CoreServices.h>

int main (int argc, char ** argv ) {
    CFHostRef host;
    dispatch_time_t timeout;
    CFAbsoluteTime startTime;
    dispatch_queue_t timeoutQueue;

    startTime = CFAbsoluteTimeGetCurrent();

    host = CFHostCreateWithName(kCFAllocatorDefault, CFSTR("www.apple.com"));
    assert(host);

    timeout = dispatch_time(DISPATCH_TIME_NOW, 5 * 1000 * 1000 * 1000ull);
    timeoutQueue = dispatch_get_global_queue(
        DISPATCH_QUEUE_PRIORITY_DEFAULT, 0
    );
    assert(timeoutQueue);

    dispatch_after(timeout, timeoutQueue,
        ^{
            printf("%u: Timeout limit reached, canceling.\n",
                (unsigned)(CFAbsoluteTimeGetCurrent() - startTime)
            );
            CFHostCancelInfoResolution(host, kCFHostAddresses);
        }
    );

    printf("%u: Starting name resolution.\n",
        (unsigned)(CFAbsoluteTimeGetCurrent() - startTime)
    );
    CFHostStartInfoResolution(host, kCFHostAddresses, NULL);
    printf("%u: Name resolution terminated.\n",
        (unsigned)(CFAbsoluteTimeGetCurrent() - startTime)
    );

    return 0;
}

如果 DNS 服务器配置正确,此代码将很快解析名称:

0: Starting name resolution.
0: Name resolution terminated.

这是意料之中的。但是,如果我在系统中“错误配置”了 DNS,那么所有 DNS 查询都会超时,这就是我得到的结果:

0: Starting name resolution.
5: Timeout limit reached, canceling.
30: Name resolution terminated.

5秒后取消计时器被击中,我取消了请求,但请求不会停止,它会再阻塞25秒。实际上,如果我不取消请求,它也会阻塞 30 秒,因为正如我上面所说,这是我系统的自然 DNS 超时。所以对的调用CFHostCancelInfoResolution完全没有任何作用。

引用 Apple 的CFHost文档:

CFHostStartInfoResolution

[...]

在同步模式下,此函数会一直阻塞,直到解析完成,在这种情况下,此函数返回 TRUE,直到通过从另一个线程调用 CFHostCancelInfoResolution 来停止解析,在这种情况下,此函数返回 FALSE,或者直到发生错误。

好的,我正在从另一个线程调用 CFHostCancelInfoResolution,但该函数一直处于阻塞状态。这要么是 API 中的错误,要么是文档中的错误,或者我太愚蠢而无法正确使用此 API,并且我在这里忽略了一些非常基本的东西。

更新

这实际上可能是一个错误。我刚刚在 10.6 上测试了上面的代码,它完全按预期工作,查找在 5 秒后被取消。在 10.7 和 10.8 上,取消调用不执行任何操作,代码会阻塞,直到达到正常的 DNS 超时。

4

0 回答 0