iOS 上的 DNS 解析与 IPv6 只能同步吗?
(这是一项正在进行的工作。我不知道是否有人可以改进它)
在 Objective C 中,使用 NSHost 解析主机名很容易。
[[NSHost hostWithName:@"www.google.com"] address]
遗憾的是 iOS (iPhone) 仅包含 NSHost 的私有版本。
我发现了许多使用其他对象或方法执行此操作的方法,但所有这些方法在结果中都只获得了 IPv4 地址。所以这是目前我发现的唯一有效的方法。
我首先尝试使用异步 CFHostStartInfoResolution ,就像 bdunagan,但未能适配IPv6。
你们中的一些人会很高兴让一种方法起作用,所以这里是一个,但是如果您知道一种异步方法,我很乐意了解它......因为目前我使用弹出窗口来警告下一次冻结这可能会发生在缓慢的蜂窝连接中
/**
Give the IPs corresponding to a Hostname
Sometime only 1 IPv4 is shown even if there's more.
Sometime only 1 IPv6 is shown even if there's more.
Certainly due to iOS Memory optimisation when locally cached
@author Christian Gonzalvez, http://wiki.gonzofamily.com
@param hostName A hostname
@return an Array of NSString of all the corresponding IP addresses. The first
is the Canonical name, the following are IPs (all NSString)
*/
+ (NSArray *)addressesForHostname:(NSString *)hostname
{
const char* hostnameC = [hostname UTF8String];
struct addrinfo hints, *res;
struct sockaddr_in *s4;
struct sockaddr_in6 *s6;
int retval;
char buf[64];
NSMutableArray *result; //the array which will be return
NSMutableArray *result4; //the array of IPv4, to order them at the end
NSString *previousIP = nil;
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = PF_UNSPEC;//AF_INET6;
hints.ai_flags = AI_CANONNAME;
//AI_ADDRCONFIG, AI_ALL, AI_CANONNAME, AI_NUMERICHOST
//AI_NUMERICSERV, AI_PASSIVE, OR AI_V4MAPPED
retval = getaddrinfo(hostnameC, NULL, &hints, &res);
if (retval == 0)
{
if (res->ai_canonname)
{
result = [NSMutableArray arrayWithObject:[NSString stringWithUTF8String:res->ai_canonname]];
}
else
{
//it means the DNS didn't know this host
return nil;
}
result4= [NSMutableArray array];
while (res) {
switch (res->ai_family){
case AF_INET6:
s6 = (struct sockaddr_in6 *)res->ai_addr;
if(inet_ntop(res->ai_family, (void *)&(s6->sin6_addr), buf, sizeof(buf))
== NULL)
{
NSLog(@"inet_ntop failed for v6!\n");
}
else
{
//surprisingly every address is in double, let's add this test
if (![previousIP isEqualToString:[NSString stringWithUTF8String:buf]]) {
[result addObject:[NSString stringWithUTF8String:buf]];
}
}
break;
case AF_INET:
s4 = (struct sockaddr_in *)res->ai_addr;
if(inet_ntop(res->ai_family, (void *)&(s4->sin_addr), buf, sizeof(buf))
== NULL)
{
NSLog(@"inet_ntop failed for v4!\n");
}
else
{
//surprisingly every address is in double, let's add this test
if (![previousIP isEqualToString:[NSString stringWithUTF8String:buf]]) {
[result4 addObject:[NSString stringWithUTF8String:buf]];
}
}
break;
default:
NSLog(@"Neither IPv4 nor IPv6!");
}
//surprisingly every address is in double, let's add this test
previousIP = [NSString stringWithUTF8String:buf];
res = res->ai_next;
}
}else{
NSLog(@"no IP found");
return nil;
}
return [result arrayByAddingObjectsFromArray:result4];
}
注意:我注意到大多数时候只返回 1 个 IPv6,我怀疑这是由于本地缓存时 iOS 内存优化造成的。如果您一次又一次地运行此方法,有时您有 3 个 IPv6,但随后您只有 1 个 IPv4。
(This is a work in progress. I wonder if someone could to improve it)
in Objective C, it's easy to resolve a hostname with NSHost.
[[NSHost hostWithName:@"www.google.com"] address]
Sadly iOS (iPhone) contains only a private version of NSHost.
I found many ways of doing this with other Objects or methods, but all of them got only IPv4 addresses in the results. So here is for the moment the only efficient method I have found.
I first tried to use the asynchronous CFHostStartInfoResolution as did bdunagan, but failed to adapt it to IPv6.
Some of you will appreciate to get a method working, so here is one, but if you know a way which would be Asynchronous I would appreciate to learn about it... cause for the moment I use a Popup to alert about the next freeze that could occur with slow cellular connection
/**
Give the IPs corresponding to a Hostname
Sometime only 1 IPv4 is shown even if there's more.
Sometime only 1 IPv6 is shown even if there's more.
Certainly due to iOS Memory optimisation when locally cached
@author Christian Gonzalvez, http://wiki.gonzofamily.com
@param hostName A hostname
@return an Array of NSString of all the corresponding IP addresses. The first
is the Canonical name, the following are IPs (all NSString)
*/
+ (NSArray *)addressesForHostname:(NSString *)hostname
{
const char* hostnameC = [hostname UTF8String];
struct addrinfo hints, *res;
struct sockaddr_in *s4;
struct sockaddr_in6 *s6;
int retval;
char buf[64];
NSMutableArray *result; //the array which will be return
NSMutableArray *result4; //the array of IPv4, to order them at the end
NSString *previousIP = nil;
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = PF_UNSPEC;//AF_INET6;
hints.ai_flags = AI_CANONNAME;
//AI_ADDRCONFIG, AI_ALL, AI_CANONNAME, AI_NUMERICHOST
//AI_NUMERICSERV, AI_PASSIVE, OR AI_V4MAPPED
retval = getaddrinfo(hostnameC, NULL, &hints, &res);
if (retval == 0)
{
if (res->ai_canonname)
{
result = [NSMutableArray arrayWithObject:[NSString stringWithUTF8String:res->ai_canonname]];
}
else
{
//it means the DNS didn't know this host
return nil;
}
result4= [NSMutableArray array];
while (res) {
switch (res->ai_family){
case AF_INET6:
s6 = (struct sockaddr_in6 *)res->ai_addr;
if(inet_ntop(res->ai_family, (void *)&(s6->sin6_addr), buf, sizeof(buf))
== NULL)
{
NSLog(@"inet_ntop failed for v6!\n");
}
else
{
//surprisingly every address is in double, let's add this test
if (![previousIP isEqualToString:[NSString stringWithUTF8String:buf]]) {
[result addObject:[NSString stringWithUTF8String:buf]];
}
}
break;
case AF_INET:
s4 = (struct sockaddr_in *)res->ai_addr;
if(inet_ntop(res->ai_family, (void *)&(s4->sin_addr), buf, sizeof(buf))
== NULL)
{
NSLog(@"inet_ntop failed for v4!\n");
}
else
{
//surprisingly every address is in double, let's add this test
if (![previousIP isEqualToString:[NSString stringWithUTF8String:buf]]) {
[result4 addObject:[NSString stringWithUTF8String:buf]];
}
}
break;
default:
NSLog(@"Neither IPv4 nor IPv6!");
}
//surprisingly every address is in double, let's add this test
previousIP = [NSString stringWithUTF8String:buf];
res = res->ai_next;
}
}else{
NSLog(@"no IP found");
return nil;
}
return [result arrayByAddingObjectsFromArray:result4];
}
NB: I noticed that most of the time only 1 IPv6 is returned, I suspect it's due to iOS Memory optimisation when locally cached. if you run this method again and again, sometime you have 3 IPv6, but then you have only 1 IPv4.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您希望方法在后台线程上运行,最简单的方法是使用
performSelectorInBackground:withObject:
;这是 NSObject 的实例方法,因此任何对象都可以使用它而无需任何额外的工作(有趣的是,包括 class 对象,这在这种情况下很好,因为这是类方法):在方法内部,您需要为线程设置一个自动释放池。您还需要设置某种回调方法来将返回值返回到主线程。确保您没有尝试在后台线程上执行任何 GUI 活动。只有在主线程上执行此操作才安全。
如果您一开始不在主线程上,或者需要更多控制,您还可以查看
NSOperation
,它更强大,因此需要更多工作。不过,它仍然比显式线程管理更容易!希望能解决您的问题。听起来你有这个方法做你需要的事情,你只需要它不阻塞主线程。
If you want a method to run on a background thread, the simplest way is to use
performSelectorInBackground:withObject:
; this is an instance method ofNSObject
, so any object can use it without any extra work (including, interestingly enough, class objects, which is good in this case because this is a class method):Inside the method, you will need to set up an autorelease pool for the thread. You will also need some kind of callback method set up to get the return value back to your main thread. Make sure that you don't try to do any GUI activity on the background thread. It's only safe to do that on the main thread.
If you were not on the main thread to begin with, or if you needed more control, you could also look into
NSOperation
, which is more powerful and therefore requires more work. It's still easier than explicit thread management, though!Hope that solves your problem. It sounded like you have this method doing what you need, you just need it to not block the main thread.
感谢 Josh,我可以做到这一点,但这是我必须做的:
不是直接调用,而是
我
调用并创建 3 个新方法:
编辑:
在实践中,我在模型中使用了所有这些,所以当我在分辨率结束之前关闭视图时,我发生了崩溃
所以在视图中我在 dealloc 中添加了避免崩溃所需的内容
然后 - 在模型中 - 我测试在对其进行任何操作之前进行委托。
thanks to Josh I could do it, but here is what I had to do :
Instead of calling directly
I call
And create 3 new methods:
Edit:
In practice I use all of that in a Model, so I had a crash when I close the View before the end of the resolution
So in the view I added in dealloc what is necessary to avoid a crash
Then - in the model - I test delegate before doing anything with it.