Как получить мой IP-адрес программно на iOS / macOS? - PullRequest
121 голосов
/ 16 августа 2011

Я хотел бы получить IP-адрес моего iPad программно. Как я могу запросить сетевую подсистему, чтобы узнать, каковы мои адреса IPv4 (и IPv6)?

PS: можно ли как-то отключить IPv6?

Ответы [ 9 ]

130 голосов
/ 29 мая 2012

Следующий код находит все адреса IPv4 и IPv6 на устройстве iOS или OSX.Первый метод getIPAddress действует более или менее как старый код в этом ответе: вы можете предпочесть либо тот, либо другой тип адреса, и он всегда предпочитает WIFI сотовой сети (очевидно, вы можете изменить это).

Более интересно то, что он может возвращать словарь всех найденных адресов, пропуская адреса для not up интерфейсов или адреса, связанные с loopback.Предыдущий код, а также другие решения по этой теме не будут правильно декодировать IPv6 (inet_ntoa не может справиться с ними).На это указал мне Jens Alfke на форуме Apple - правильная функция для использования - inet_ntop (посмотрите на страницу руководства или обратитесь к этой статье inet_ntop , также предоставленнойJens.

Ключи словаря имеют форму "interface" "/" "ipv4 или ipv6".

#include <ifaddrs.h>
#include <arpa/inet.h>
#include <net/if.h>

#define IOS_CELLULAR    @"pdp_ip0"
#define IOS_WIFI        @"en0"
//#define IOS_VPN       @"utun0"
#define IP_ADDR_IPv4    @"ipv4"
#define IP_ADDR_IPv6    @"ipv6"

- (NSString *)getIPAddress:(BOOL)preferIPv4
{
    NSArray *searchArray = preferIPv4 ?
                            @[ /*IOS_VPN @"/" IP_ADDR_IPv4, IOS_VPN @"/" IP_ADDR_IPv6,*/ IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6 ] :
                            @[ /*IOS_VPN @"/" IP_ADDR_IPv6, IOS_VPN @"/" IP_ADDR_IPv4,*/ IOS_WIFI @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4 ] ;

    NSDictionary *addresses = [self getIPAddresses];
    NSLog(@"addresses: %@", addresses);

    __block NSString *address;
    [searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
        {
            address = addresses[key];
            if(address) *stop = YES;
        } ];
    return address ? address : @"0.0.0.0";
}

- (NSDictionary *)getIPAddresses
{
    NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];

    // retrieve the current interfaces - returns 0 on success
    struct ifaddrs *interfaces;
    if(!getifaddrs(&interfaces)) {
        // Loop through linked list of interfaces
        struct ifaddrs *interface;
        for(interface=interfaces; interface; interface=interface->ifa_next) {
            if(!(interface->ifa_flags & IFF_UP) /* || (interface->ifa_flags & IFF_LOOPBACK) */ ) {
                continue; // deeply nested code harder to read
            }
            const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
            char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
            if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) {
                NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
                NSString *type;
                if(addr->sin_family == AF_INET) {
                    if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
                        type = IP_ADDR_IPv4;
                    }
                } else {
                    const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
                    if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
                        type = IP_ADDR_IPv6;
                    }
                }
                if(type) {
                    NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
                    addresses[key] = [NSString stringWithUTF8String:addrBuf];
                }
            }
        }
        // Free memory
        freeifaddrs(interfaces);
    }
    return [addresses count] ? addresses : nil;
}

EDIT1: код обновлен 16 мая 2014 года (ошибка указана lhunath, см. комментарии.) Теперь возвращаются петлевые адреса, но вы можете легко раскомментировать тест, чтобы исключить их самостоятельно.

EDIT2: (каким-то неизвестным лицом): Улучшено далее 13 марта 2015: В случае, если пользовательиспользует VPN (независимо от Wi-Fi или сотовой связи), предыдущий код потерпел бы неудачу. Теперь он работает даже с VPN-соединениями. VPN-соединения имеют приоритет перед WiFi и сотовой, потому что это устройство обрабатывает его. Это должно работать даже на Macпоскольку VPN-подключение на Mac также использует IF utun0, но не тестировалось.

EDIT3: (8/9/2016) Учитывая проблемы, с которыми сталкивается @Qiulang (см. комментарии) сh код VPN (который кто-то добавил), я закомментировал его.Если кто-то точно знает, как указать VPN пользователя, пожалуйста, оставьте комментарий.

88 голосов
/ 16 августа 2011

В вашем файле реализации .m,

#import <ifaddrs.h>
#import <arpa/inet.h>



// Get IP Address
- (NSString *)getIPAddress {    
    NSString *address = @"error";
    struct ifaddrs *interfaces = NULL;
    struct ifaddrs *temp_addr = NULL;
    int success = 0;
    // retrieve the current interfaces - returns 0 on success
    success = getifaddrs(&interfaces);
    if (success == 0) {
        // Loop through linked list of interfaces
        temp_addr = interfaces;
        while(temp_addr != NULL) {
            if(temp_addr->ifa_addr->sa_family == AF_INET) {
                // Check if interface is en0 which is the wifi connection on the iPhone
                if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {
                    // Get NSString from C String
                    address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];               
                }
            }
            temp_addr = temp_addr->ifa_next;
        }
    }
    // Free memory
    freeifaddrs(interfaces);
    return address;

} 
18 голосов
/ 06 января 2015

Было бы легко просто позвонить в одну из служб поиска IP-адресов? Я настроил http://ipof.in в качестве службы, которая возвращает IP-адрес устройства в формате JSON / XML или в виде простого текста. Вы можете найти их здесь

http://ipof.in/json

http://ipof.in/xml

http://ipof.in/txt

Если вы хотите HTTPS, вы можете использовать те же URL-адреса с префиксом https. Преимущество заключается в том, что даже если вы пользуетесь Wi-Fi, вы получите публичный адрес.

4 голосов
/ 13 апреля 2016

Многие существующие решения учитывают только беспроводные интерфейсы, которые не будут работать для проводных подключений через адаптер Ethernet (т. Е. Без Wi-Fi или 3G);посмотрите это более свежее решение, которое учитывает IP-адреса, полученные через проводные интерфейсы.

iPad: Как получить IP-адрес программно WIRED (не через беспроводную сеть)

3 голосов
/ 14 июня 2017

Получить IP-адрес с помощью Swift 3:

func getIPAddress() -> String {
    var address: String = "error"

    var interfaces: ifaddrs? = nil

    var temp_addr: ifaddrs? = nil
    var success: Int = 0
    // retrieve the current interfaces - returns 0 on success
    success = getifaddrs(interfaces)
    if success == 0 {
        // Loop through linked list of interfaces
        temp_addr = interfaces
        while temp_addr != nil {
            if temp_addr?.ifa_addr?.sa_family == AF_INET {
                // Check if interface is en0 which is the wifi connection on the iPhone
                if (String(utf8String: temp_addr?.ifa_name) == "en0") {
                    // Get NSString from C String
                    address = String(utf8String: inet_ntoa((temp_addr?.ifa_addr as? sockaddr_in)?.sin_addr))
                }
            }
            temp_addr = temp_addr?.ifa_next
        }
    }
        // Free memory
    freeifaddrs(interfaces)
    return address
}
1 голос
/ 01 сентября 2016

@ Ответ DavidH работает, пока я не получу такой результат от какой-то сотовой сети 4G:

{
    "lo0/ipv4" = "127.0.0.1";
    "lo0/ipv6" = "fe80::1";
    "pdp_ip0/ipv4" = "10.132.76.168";
    "utun0/ipv6" = "fe80::72c3:e25e:da85:b730";
}

Я не использую vpn, поэтому понятия не имею, почему у меня был utun0 / ipv6.

--- Обновлено ---

Я также отладил эту проблему и обнаружил, что могу получить поддельный vpn-адрес даже в других сетях 4G (это ошибка iOS?),

{
    ""awdl0/ipv6"" = ""fe80::c018:9fff:feb2:988"";
    ""en0/ipv6"" = ""fe80::181a:2e43:f91b:db2b"";
    ""lo0/ipv4"" = ""127.0.0.1"";
    ""lo0/ipv6"" = ""fe80::1"";
    ""pdp_ip0/ipv4"" = ""10.48.10.210"";
    ""utun0/ipv4"" = ""192.168.99.2"";
}

Если я использовал vpn, я получу это:

{
    "lo0/ipv4" = "127.0.0.1";
    "lo0/ipv6" = "fe80::1";
    "pdp_ip0/ipv4" = "10.49.187.23";
    "utun0/ipv6" = "fe80::5748:5b5d:2bf0:658d";
    "utun1/ipv4" = "192.168.99.2"; //the real one
}

Так что это utun1 НЕ utun0

Не понимая, почему мне просто нужно сбросить проверку vpn: (

---- обновление ----

Я поднял ошибку (28131847) в apple и ответил: «Не все интерфейсы utun предназначены для VPN. Существуют другие функции ОС, использующие интерфейсы utun».

Но когда я спросил, как получить действительный IP-адрес vpn, его ответ был довольно разочарован: «Вы можете перейти в Настройки -> VPN и посмотреть вашу конфигурацию VPN, чтобы увидеть, активна ли VPN. В некоторых случаях вы также можно увидеть назначенный IP-адрес. Мы закрываем этот отчет об ошибках. " (

---- обновление 2016/11/04 ----

Я снова столкнулся с проблемой, и мне нужно еще изменить @ DavidH ответ, чтобы исправить это:

Я был в сети 4G и получил этот адрес:

addresses: {
    "awdl0/ipv6" = "fe80::98fd:e6ff:fea9:3afd";
    "en0/ipv6" = "fe80::8dd:7d92:4159:170e";
    "lo0/ipv4" = "127.0.0.1";
    "lo0/ipv6" = "fe80::1";
    "pdp_ip0/ipv4" = "10.37.212.102";
    "utun0/ipv6" = "fe80::279c:ea56:a2ef:d128";
}

С его первоначальным ответом я получу wifi IP fe80 :: 8dd: 7d92: 4159: 170e, который был фальшивым, и соединение не удалось.

Итак, я изменил код, чтобы понравиться,

[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
 {
     if ((internetReach.isReachableViaWiFi && [key hasPrefix:IOS_WIFI]) ||
         (internetReach.isReachableViaWWAN && [key hasPrefix:IOS_CELLULAR])) {
         address = addresses[key];
         if(address) *stop = YES;
     }
 } ];
1 голос
/ 09 мая 2014

Этот ответ был вдохновлен ответом @ DavidH.Я исправил некоторые проблемы, заменил inet_ntop на getnameinfo, что позволяет использовать более чистый подходОбратите внимание, что это приводит к появлению словаря, который отображает имя интерфейса на массив IP-адресов (с технической точки зрения у интерфейса может быть несколько IPv4 и IPv6).Он не различает IPv4 и IPv6:

  // Get all our interface addresses.
  struct ifaddrs *ifAddresses;
  if (getifaddrs( &ifAddresses ) != 0) {
    NSLog( @"Couldn't get interface addresses: %d", errno );
    return nil;
  }

  int error;
  char host[MAX( INET_ADDRSTRLEN, INET6_ADDRSTRLEN )];
  _ipAddressesByInterface = [NSMutableDictionary dictionaryWithCapacity:8];

  for (struct ifaddrs *ifAddress = ifAddresses; ifAddress; ifAddress = ifAddress->ifa_next) {
    if (!(ifAddress->ifa_flags & IFF_UP) || (ifAddress->ifa_flags & IFF_LOOPBACK))
      // Ignore interfaces that aren't up and loopback interfaces.
      continue;

    if (ifAddress->ifa_addr->sa_family != AF_INET && ifAddress->ifa_addr->sa_family != AF_INET6)
      // Ignore non-internet addresses.
      continue;

    if ((error = getnameinfo( ifAddress->ifa_addr, ifAddress->ifa_addr->sa_len, host, sizeof( host ), NULL, 0, NI_NUMERICHOST )) != noErr) {
      // Couldn't to format host name for this address.
      NSLog( @"Couldn't resolve host name for address: %s", gai_strerror( error ) );
      continue;
    }

    NSString *ifName = [NSString stringWithCString:ifAddress->ifa_name encoding: NSUTF8StringEncoding];
    NSMutableArray *ifIpAddresses = _ipAddressesByInterface[ifName];
    if (!ifIpAddresses)
      ifIpAddresses = _ipAddressesByInterface[ifName] = [NSMutableArray arrayWithCapacity:2];
    [ifIpAddresses addObject:[NSString stringWithCString:host encoding: NSUTF8StringEncoding]];
  }

  freeifaddrs( ifAddresses );
  return _ipAddressesByInterface;
1 голос
/ 19 декабря 2013

Текущее решение не возвращает устройство en0 в OS X, следующий код использует платформу System Configuration Framework для получения интерфейсов, а затем использует стандартные функции C для получения IP-адреса.

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <net/if.h>
#define IFT_ETHER 0x6

#include <SystemConfiguration/SCDynamicStore.h>

+(void)getInterfaces
{
    SCDynamicStoreRef storeRef = SCDynamicStoreCreate(NULL, (CFStringRef)@"FindCurrentInterfaceIpMac", NULL, NULL);
    CFPropertyListRef global = SCDynamicStoreCopyValue (storeRef,CFSTR("State:/Network/Interface"));
    id primaryInterface = [(__bridge NSDictionary *)global valueForKey:@"Interfaces"];

    for (NSString* item in primaryInterface)
    {
        if(get_iface_address([item UTF8String]))
        {
            NSString *ip = [NSString stringWithUTF8String:get_iface_address([item UTF8String])];
            NSLog(@"interface: %@ - %@",item,ip);
        } else
            NSLog(@"interface: %@",item);
    }
}

static char * get_iface_address (char *interface)
{
    int sock;
    uint32_t ip;
    struct ifreq ifr;
    char *val;

    if (!interface)
        return NULL;

    /* determine UDN according to MAC address */
    sock = socket (AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
        perror ("socket");
        return NULL;
    }

    strcpy (ifr.ifr_name, interface);
    ifr.ifr_addr.sa_family = AF_INET;

    if (ioctl (sock, SIOCGIFADDR, &ifr) < 0)
    {
        perror ("ioctl");
        close (sock);
        return NULL;
    }

    val = (char *) malloc (16 * sizeof (char));
    ip = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;
    ip = ntohl (ip);
    sprintf (val, "%d.%d.%d.%d",
             (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF);

    close (sock);

    return val;
}
0 голосов
/ 24 июня 2017

Отличное решение для быстрого ввода Этот файл , который обслуживает все детали.

В одном из моих приложений мне нужно получить IP-адрес Wi-Fi.Я использовал ответы выше, в быстром 3, как это:

let WIFI_IF = "en0"
let UNKNOWN_IP_ADDRESS = ""
var addresses: [AnyHashable: Any] = ["wireless": UNKNOWN_IP_ADDRESS, "wired": UNKNOWN_IP_ADDRESS, "cell": UNKNOWN_IP_ADDRESS]
var interfaces: UnsafeMutablePointer<ifaddrs>? = nil
var temp_addr: UnsafeMutablePointer<ifaddrs>? = nil
var success: Int = 0
success = Int(getifaddrs(&interfaces))
if success == 0 {
   temp_addr = interfaces
   while temp_addr != nil {
      if temp_addr?.pointee.ifa_addr == nil {
           continue
      }
      if temp_addr?.pointee.ifa_addr.pointee.sa_family == UInt8(AF_INET) {
         if (String(utf8String: (temp_addr?.pointee.ifa_name)!) == WIFI_IF) {
             addresses["wireless"] = String(utf8String: inet_ntoa(((temp_addr?.pointee.ifa_addr as? sockaddr_in)?.sin_addr)!))
         }
      }
      temp_addr = temp_addr?.pointee.ifa_next
   }
}

В этом коде происходит сбой, потому что я должен проверить для nil в каждом утверждении, которое я использовал как необязательный с ?.Так что для меня лучше использовать данный связанный файл в моем классе.Теперь мне легко проверить, как:

class func getWifiIPAddress() -> String {
    var wifiIp = ""

    let WIFI_IF = "en0"
    let allInterface = Interface.allInterfaces()

    for interf in allInterface {
        if interf.name == WIFI_IF {
            if let address = interf.address {

                if address.contains(".") {
                wifiIp = address
                break
                }
            }
        }
    }

    return wifiIp
}

Я проанализировал строку для ".", потому что интерфейс класса возвращает два интерфейса в моем iPhone для en0 адреса, например, "fb00 ::", и адреса, как"101.10.1.1"

...