[ios] iOS / macOS에서 프로그래밍 방식으로 내 IP 주소를 얻는 방법은 무엇입니까?

프로그래밍 방식으로 내 iPad의 IP 주소를 얻고 싶습니다. 내 IPv4 (및 IPv6) 주소가 무엇인지 확인하기 위해 네트워킹 하위 시스템을 쿼리하려면 어떻게해야합니까?

PS : 어떻게 든 IPv6를 비활성화 할 수 있습니까?



답변

다음 코드는 iOS 또는 OSX 장치에서 모든 IPv4 및 IPv6 주소를 찾습니다. 첫 번째 getIPAddress방법은이 답변의 이전 코드처럼 다소간 작동합니다. 하나 또는 다른 유형의 주소를 선호 할 수 있으며 항상 셀룰러보다 WIFI를 선호합니다 (분명히 이것을 변경할 수 있음).

더 흥미롭게도 발견 된 모든 주소의 사전을 반환하고 not up인터페이스의 주소를 건너 뛰 거나와 관련된 주소를 반환 할 수 있습니다 loopback. 이 주제에 대한 이전 코드와 다른 솔루션은 IPv6를 올바르게 디코딩하지 않습니다 (inet_ntoa는이를 처리 할 수 ​​없습니다). 이것은 Apple 포럼에서 Jens Alfke 가 저에게 지적했습니다 . 적절한 기능은 inet_ntop입니다 (맨 페이지를 보거나 Jens가 제공하는 이 inet_ntop 기사를 참조하십시오 .

사전 키의 형식은 “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 : 코드가 2014 년 5 월 16 일에 업데이트되었습니다 (Lhunath가 지적한 버그, 주석 참조). 이제 루프백 주소가 반환되었지만 테스트의 주석 처리를 제거하여 직접 제외 할 수 있습니다.

EDIT2 : (알 수없는 사람에 의해) : 2015 년 3 월 13 일 추가 개선 : 사용자가 VPN을 사용하는 경우 (WiFi 또는 셀룰러를 통해) 이전 코드가 실패했을 것입니다. 이제 VPN 연결에서도 작동합니다. VPN 연결은 장치가 처리하는 방식이므로 WiFi 및 셀보다 우선합니다. Mac의 VPN 연결도 IF utun0을 사용하지만 테스트되지 않았기 때문에 Mac에서도 작동합니다.

EDIT3 : (2016 년 9 월 8 일) VPN 코드 (다른 사람이 추가 한)로 @Qiulang (댓글 참조)이 경험 한 문제를 감안할 때 주석 처리했습니다. 사용자 VPN을 지정하는 방법을 확실히 아는 사람이 있으면 댓글을 달아주세요.


답변

구현 파일 .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;

} 


답변

기존의 많은 솔루션은 이더넷 어댑터 (예 : Wi-Fi 또는 3G 없음)를 통한 유선 연결에는 작동하지 않는 무선 인터페이스 만 고려합니다. 유선 인터페이스를 통해 얻은 IP 주소도 고려하는이 ​​최신 솔루션을 참조하십시오.

iPad : 프로그래밍 방식으로 WIRED IP 주소를 얻는 방법 (무선을 통하지 않음)


답변

Swift 3을 사용하여 IP 주소 가져 오기 :

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
}


답변

현재 솔루션은 OS X에서 en0 장치를 반환하지 않습니다. 다음 코드는 시스템 구성 프레임 워크를 사용하여 인터페이스를 가져온 다음 표준 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;
}


답변

이 답변은 @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;


답변

@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을 사용했는지 모르겠습니다.

— 업데이트 됨 —

이 문제를 추가로 디버깅하고 다른 4G 네트워크에서도 가짜 VPN 주소를 얻을 수 있음을 발견했습니다 (이 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
}

따라서 utun0이 아닌 utun1입니다.

왜 VPN 검사를 중단해야하는지 알지 못하고 🙁

—- 업데이트 —-

Apple에 버그 (28131847)를 제기하고 “모든 utun 인터페이스가 VPN 용은 아닙니다. utun 인터페이스를 사용하는 다른 OS 기능이 있습니다.”라고 답했습니다.

그러나 내가 유효한 VPN IP 주소를 얻는 방법을 물었을 때, 그들의 대답은 다소 실망 스러웠습니다. “설정-> 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;
     }
 } ];