AFAIK CFHost
在OS X(和iOS)上提供唯一的公共API ,用于解析异步和/或可以取消的DNS解析(因此可以实现自定义超时) )。所有其他API都是同步的,无法取消,因此每个DNS查找都必须浪费一个线程以使操作异步或可停止(即使是Grand Central Dispatch也会在每个查询中浪费一个线程,你只需要不必自己创建线程。每个DNS解析调用有一个阻塞线程(并且这样的调用可以阻塞相当长的时间,在我的系统上超时是在调用最终超时之前的30秒),如果你需要解析大量的DNS,那真的不是一种方法主机名。
CFHost
对我来说似乎是一个很好的工作。它可以同步使用,在这种情况下,文档说阻塞请求可以从另一个线程中取消,它可以异步使用,在这种情况下请求在后台运行,如果需要也可以取消,但它在成功或自然超时之前不会阻止任何线程。内部CFHost
使用getaddrinfo_async_*
函数,但这不是公共API,据我所知,这些函数是私有的,不应该直接使用它们。
所以这里是我编写的一个简单的代码,用于使用cancel来测试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
的调用完全是 NOTHING 。
引用Apple的CFHost
文档:
CFHostStartInfoResolution
[...]
在同步模式下,此功能会一直阻止,直到分辨率为止 完成后,在这种情况下此函数返回TRUE,直到 通过调用CFHostCancelInfoResolution来停止分辨率 另一个线程,在这种情况下,此函数返回FALSE,或直到 发生错误。
好的,我我从另一个线程调用CFHostCancelInfoResolution,但该函数仍然阻塞。这可能是API中的错误,文档中的错误,或者我是非常愚蠢的正确使用此API,并且有一些非常基本的我在这里忽略。
这实际上可能是一个错误。我刚刚在10.6上测试了上面的代码,它完全按预期工作,5秒后取消查找。在10.7和10.8上,取消调用不执行任何操作,代码将阻塞,直到达到正常的DNS超时。