2011-05-20 312 views
2

(這是一項正在進行的工作。我不知道是否有人能對其進行改進)的目標C在iOS上使用IPv6的DNS解析僅同步?

,可以很容易地解決與NSHost主機名。

[[NSHost hostWithName:@"www.google.com"] address] 

很遺憾,iOS(iPhone)僅包含一個私有版本的NSHost。

我發現有很多方法可以與其他對象或方法做到這一點,但它們都只在結果中獲得IPv4地址。所以這裏是目前唯一有效的方法。

我第一次嘗試使用異步CFHostStartInfoResolution與bdunagan一樣,但未能使其適應IPv6。

你們中的一些人會欣賞得到一個方法的工作,所以這裏是一個,但如果你知道一種方法,這將是異步的,我會很高興瞭解它...因爲我現在使用Popup來提醒關於可能緩慢蜂窩連接發生

/** 
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的下一個凍結:我注意到,大多數的只返回1 IPv6的時候,我懷疑這是由於iOS的內存優化時,本地緩存。如果你一次又一次地運行這個方法,有時候你有3個IPv6,但是你只有1個IPv4。

+0

...爲什麼你不能只在後臺線程上執行該選擇器?也許我有點密... – lxt 2011-05-20 07:20:29

+0

我開始閱讀關於線程的Apple文檔,它們(沒有)密集。還沒有找到一個簡單的例子。你知道線程嗎?我開始認爲這是不值得的複雜的文件所需要的時間。如果你認爲這是解決方案,我會做,並糾正這篇文章。 – chriscatfr 2011-05-20 11:55:10

回答

1

如果你想要一個方法在後臺線程上運行,最簡單的方法是使用performSelectorInBackground:withObject:;這是NSObject實例方法,所以任何對象都可以使用它沒有任何額外的工作(包括,有趣的是,對象,在這種情況下很好的,因爲這是一個類的方法):

[[self class] performSelectorInBackground:@selector(addressesForHostName:) 
           withObject:theHostName]; 

在該方法中,您需要爲該線程設置一個自動釋放池。您還需要設置某種回調方法,以將返回值返回到主線程。確保您不要在後臺線程上執行任何GUI活動。在主線上這樣做只是安全的。

+ (NSArray *)addressesForHostname:(NSString *)hostname 
{ 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    // Do your stuff... 

    // Wait until done to allow memory to be managed properly 
    // If we did not do this, the array might be deallocated 
    // before the main thread had a chance to retain it 
    [self performSelectorOnMainThread:@selector(addressesCallback:) 
           withObject:[result arrayByAddingObjectsFromArray:result4] 
          waitUntilDone:YES]; 
    // Inside a class method, self refers to the class object. 

    [pool drain]; 
} 

如果你不是在主線程開始說起,或者如果你需要更多的控制,你也可以看看NSOperation,這是更強大,因此需要更多的工作。不過,它仍然比顯式線程管理更簡單!

希望能夠解決您的問題。這聽起來像你有這種方法做你所需要的,你只需要它不會阻塞主線程。

+0

完美的謝謝你,我正在閱讀文檔中的十幾頁,你的簡單例子是一開始就更清晰,我會看看NSOperation。一旦我做到了,我編輯這個問題。 – chriscatfr 2011-05-21 05:20:52

+0

很高興能幫到你!你應該保持原樣。 StackOverflow格式用於發佈問題以保留單個特定問題並回答帖子以包含解決方案。只有在需要澄清或添加細節時才編輯您的問題,以便得到更好的答案。如果你想_me_特別澄清或擴大我的答案的某些元素,請在評論中提問。 – 2011-05-21 05:42:20

1

感謝約什 - 我能做到這一點,但這裏是我必須做的:

不是直接調用

self.ipAddressesString = [CJGIpAddress addressesForHostname:@"www.google.com"]; 

我打電話

[self resolveNow:@"www.google.com"]; 

,創造3種新方法:

- (void)resolveNow:(NSString *)hostname 
{ 
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 
    [self performSelectorInBackground:@selector(hostname2ipAddresses:) 
            withObject:hostname]; 
} 

- (void)hostname2ipAddresses:(NSString *)hostname 
{ 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 
     //Here is my previous lonely line !! safely started in an other thread 
    self.ipAddressesString = [CJGIpAddress addressesForHostname:hostname]; 
    [self performSelectorOnMainThread:@selector(resolutionDidFinish) 
          withObject:nil 
         waitUntilDone:YES]; 
    [pool drain]; 
} 

- (void)resolutionDidFinish 
{ 
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 
    //My STUFF with self.ipAddressesString (now filled) 
} 

編輯: 在實踐中,我在模型中使用所有這些,所以當我在分辨率結束前關閉視圖時崩潰了

因此,在視圖中,我在dealloc中添加了避免碰撞

- (void)dealloc 
{ 
    self.model.delegate = nil; 
    [super dealloc]; 
} 

然後 - 在模型中 - 我在做任何事情之前測試委託。