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 超时。