Làm cách nào để lấy địa chỉ IP của tôi theo chương trình trên iOS / macOS?


124

Tôi muốn lấy địa chỉ IP của iPad theo chương trình. Làm cách nào tôi có thể truy vấn hệ thống con mạng để tìm hiểu địa chỉ IPv4 (và IPv6) của tôi là gì?

PS: Tôi có thể tắt IPv6 bằng cách nào đó không?


14
Liên quan đến 'PS' của bạn ở trên, vui lòng không vô hiệu hóa lập trình IPv6 trên thiết bị của ai đó. Nó chỉ đơn giản là thô lỗ.
Jeremy Visser

Bạn không thể tắt IPv6. Nó là bắt buộc. Trên thực tế, ứng dụng iOS của bạn phải hỗ trợ IPv6.
Michael Hampton

Câu trả lời:


132

Đoạn mã sau tìm thấy tất cả địa chỉ IPv4 và IPv6 trên thiết bị iOS hoặc OSX. getIPAddressPhương thức đầu tiên hoạt động ít nhiều như mã cũ hơn trong câu trả lời này: bạn có thể thích một hoặc một địa chỉ loại khác và nó luôn thích WIFI hơn di động (rõ ràng bạn có thể thay đổi điều này).

Thú vị hơn, nó có thể trả về một từ điển của tất cả các địa chỉ được tìm thấy, bỏ qua địa chỉ cho các not upgiao diện hoặc địa chỉ được liên kết loopback. Mã trước đó cũng như các giải pháp khác về chủ đề này sẽ không giải mã đúng IPv6 (inet_ntoa không thể xử lý chúng). Điều này đã được Jens Alfke chỉ ra cho tôi trên một diễn đàn của Apple - chức năng phù hợp để sử dụng là inet_ntop (xem trang người đàn ông và tham khảo bài viết inet_ntop này cũng do Jens cung cấp.

Các khóa từ điển có dạng "giao diện" "/" "ipv4 hoặc 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: Mã được cập nhật vào ngày 16 tháng 5 năm 2014 (lỗi được chỉ ra bởi lhunath, xem bình luận). Địa chỉ Loopback hiện đã được trả lại, nhưng bạn có thể dễ dàng bỏ qua bài kiểm tra để tự loại trừ chúng.

EDIT2: (bởi một số người chưa biết): Đã cải thiện hơn nữa vào ngày 13 tháng 3 năm 2015: Trong trường hợp người dùng sử dụng VPN (bất kể qua WiFi hoặc Cellular), mã trước đó sẽ không thành công. Bây giờ, nó hoạt động ngay cả với các kết nối VPN. Kết nối VPN được ưu tiên hơn WiFi và Cell vì đó là cách thiết bị xử lý nó. Điều này thậm chí sẽ hoạt động cho máy Mac vì kết nối VPN trên máy Mac cũng đang sử dụng IF utun0 nhưng chưa được thử nghiệm.

EDIT3: (9/8/2016) Với các vấn đề mà @Qiulang (xem bình luận) gặp phải với mã VPN (mà người khác đã thêm), tôi đã nhận xét nó. Nếu bất cứ ai biết chắc chắn làm thế nào để chỉ định VPN người dùng, vui lòng bấm nút bình luận.


Làm thế nào thường addrđược NULL? người dùng của tôi thỉnh thoảng nhận được NULL. Bạn có biết những lý do có thể?
HelmiB

Tôi chỉ sử dụng AF_INETđể kiểm tra. có thể là cái này?
HelmiB

1
Tôi không biết, nhưng chắc chắn người dùng của bạn có thể truy cập mạng IPV6.
David H

2
Đây phải là câu trả lời "đúng" vì nó hoàn chỉnh với 3G. Cảm ơn các cập nhật.
palme

1
Tôi không nghĩ rằng điều này hoạt động chính xác với IPv6. Hàm inet_ntoa lấy địa chỉ IPv4, do đó, trong trường hợp sa_type == AF_INET6, bạn đang lấy địa chỉ IPv6 và nhập địa chỉ đó thành IPv4 và chuyển chuỗi đó thành chuỗi (về cơ bản từ 32 bit cao của 128 bit địa chỉ.)
Jens Alfke

88

Trong tệp thực hiện của bạn .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;

} 

1
Không có giao diện en0 xuất hiện trong danh sách giao diện, vì vậy chỉ trả về lỗi. Không chắc chắn nếu có gì đó đã thay đổi nhưng chắc chắn không hoạt động với tôi trên iOS 5.x
wuf810

11
Nếu bạn nhìn bên dưới, bạn sẽ thấy một số mã được sửa đổi một chút trả về địa chỉ ô (3G) nếu WIFI bị tắt.
David H

1
Đoạn mã sau chỉ cung cấp địa chỉ IPv4 của en0. Còn IPv6 thì sao? Ai đó có thể đưa ra một mẫu mã về cách lấy nó không?
Oded Regev

2
Điều này không tìm thấy địa chỉ IP khi thiết bị không được kết nối với wifi, nhưng với mạng di động
Mapedd

1
Sửa đổi điều kiện if bên dưới if ([[NSString chuỗiWithUTF8String: temp_addr-> ifa_name] isEqualToString: @ "en0"]) thành if ([[NSString chuỗiWithUTF8String: temp_addr- chuỗiWithUTF8String: temp_addr-> ifa_name] isEqualToString: @ "pdp_ip0"]) để lấy IP khi thiết bị không được kết nối với WIFI ..
Raju

4

Nhiều giải pháp hiện tại chỉ xem xét các giao diện không dây, sẽ không hoạt động đối với các kết nối có dây thông qua bộ điều hợp Ethernet (tức là không có Wifi hoặc 3G); xem giải pháp mới hơn này xem xét các địa chỉ IP thu được thông qua giao diện có dây.

iPad: Cách nhận địa chỉ IP được lập trình WIRED (không qua mạng không dây)


Thông minh! Tôi sẽ sớm thêm mã này vào mã của tôi ở trên. En1 là gì - bạn có biết?
David H

1
Tôi không, nhưng tôi đã nhận thấy đó không phải là gia đình AF_INET như các giao diện en khác
lundhjem

1
@DavidH gần đây IP ethernet đã hiển thị trên en1 trên một số iPad nhất định, do đó có vẻ như nó cũng là một thành viên của gia đình AF_INET trong một số trường hợp nhất định. Bạn nên kiểm tra giao diện đó là tốt.
lundhjem

3

Nhận địa chỉ IP bằng 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

Giải pháp hiện tại không trả về thiết bị en0 trên OS X, đoạn mã sau sử dụng Khung cấu hình hệ thống để nhận các giao diện sau đó sử dụng các hàm C tiêu chuẩn để lấy địa chỉ 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;
}

Đáng buồn thay, SCD liteStoreCreate không có sẵn trên iOS, cũng như: SCD liteStoreCopyValue
Alex Zavatone

1

Câu trả lời này được lấy cảm hứng từ câu trả lời của @ DavidH. Tôi đã sửa một số vấn đề, thay thế inet_ntopbằng cách getnameinfocho phép một cách tiếp cận sạch hơn. Lưu ý rằng điều này mang lại một từ điển ánh xạ tên giao diện thành một mảng địa chỉ IP (một giao diện có thể có nhiều IPv4 và IPv6 được liên kết với nó, về mặt kỹ thuật). Nó không phân biệt giữa IPv4 và 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;

Tôi chưa bao giờ thấy strf- đó có phải là một hàm trợ giúp mà bạn có trong mã của mình không?
David H

Vâng, xin lỗi, có một vài người trợ giúp trong đoạn mã trên. errstrf. Bạn chỉ có thể thay thế strfbằng -stringWithCString:encoding:. errlà một NSLogtrình bao bọc cũng xuất ra tệp & dòng. github.com/Lyndir/Pearl/blob/master/Pearl/ Khăn
lhunath

1

Câu trả lời của DavidH hoạt động tốt cho đến khi tôi nhận được kết quả này từ một số mạng di động 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";
}

Tôi không sử dụng vpn vì vậy tôi không biết tại sao tôi lại có utun0 / ipv6.

--- Đã cập nhật ---

Tôi tiếp tục gỡ lỗi vấn đề này và thấy rằng tôi có thể nhận được địa chỉ vpn giả ngay cả trong các mạng 4G khác (đây có phải là lỗi iOS không ??),

{
    ""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"";
}

Nếu tôi đã sử dụng vpn, tôi sẽ nhận được điều này:

{
    "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
}

Vì vậy, đó là utun1 KHÔNG utun0

Không cần tìm hiểu tại sao tôi sẽ phải bỏ kiểm tra vpn :(

---- cập nhật ----

Tôi đã đưa ra một lỗi (28131847) cho apple và trả lời "Không phải tất cả các giao diện utun đều dành cho VPN. Có những tính năng hệ điều hành khác sử dụng giao diện utun."

Nhưng khi tôi hỏi làm thế nào để có được địa chỉ IP vpn hợp lệ thì câu trả lời của họ khá thất vọng, "Bạn có thể vào Cài đặt -> VPN và xem cấu hình VPN của bạn để xem VPN có hoạt động không. Trong một số trường hợp, bạn có thể thấy cũng đã gán địa chỉ IP ở đó. Chúng tôi hiện đang đóng báo cáo lỗi này. " :

---- cập nhật 2016/11/04 ----

Tôi lại gặp sự cố và tôi cần sửa đổi thêm câu trả lời của @ DavidH để khắc phục:

Tôi đã ở trong mạng 4G và tôi đã nhận được địa chỉ này:

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";
}

Với câu trả lời ban đầu của anh ấy, tôi sẽ nhận được IP wifi8080: 8dd: 7d92: 4159: 170e, đó là giả và kết nối không thành công.

Vì vậy, tôi đã sửa đổi mã để thích,

[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;
     }
 } ];

Tôi đã đăng trên các diễn đàn nội bộ về vấn đề VPN, kỹ sư của Apple đã đề nghị giúp đỡ. Tôi cần bạn liên hệ với tôi ngoại tuyến dhoerl tại mac dot com
David H

1

Giải pháp tuyệt vời cho swift trong tập tin này phục vụ tất cả các chi tiết.

Trong một trong những ứng dụng của tôi, tôi cần tìm nạp địa chỉ IP wifi. Tôi đã sử dụng câu trả lời ở trên, trong swift 3 như thế này:

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
   }
}

Trong mã này, Nó gặp sự cố vì tôi phải kiểm tra niltrong mỗi câu lệnh tôi đã sử dụng làm tùy chọn ?. Vì vậy, tốt hơn là tôi sử dụng tệp được liên kết nhất định trong lớp của mình. Bây giờ nó trở nên dễ dàng để tôi kiểm tra như:

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
}

Tôi đã phân tích chuỗi "."vì lớp Giao diện trả về hai giao diện trong iPhone của tôi cho en0địa chỉ như "fb00 ::" và địa chỉ như "101.10.1.1"


1
Liên kết đến một giải pháp tiềm năng luôn được chào đón, nhưng vui lòng thêm ngữ cảnh xung quanh liên kết để người dùng đồng nghiệp của bạn sẽ có một số ý tưởng về nó là gì và tại sao nó lại ở đó. Luôn trích dẫn phần có liên quan nhất của một liên kết quan trọng, trong trường hợp trang đích không thể truy cập được hoặc ngoại tuyến vĩnh viễn. Hãy xem xét rằng hầu như không có nhiều hơn một liên kết đến một trang web bên ngoài là một lý do có thể là Tại sao và làm thế nào một số câu trả lời bị xóa? .
Paul Roub

1
Ok sẽ cập nhật tương ứng vì nó hữu ích cho tôi để hiểu rõ hơn
Max

1

Tôi đã tạo một tập tin đơn giản để lấy địa chỉ IP. Tôi dựa trên giải pháp này dựa trên câu trả lời của @ lundhjem, @ DavidH và @ Ihunath. Nó xem xét các kết nối có dây. Tôi chưa bao gồm VPN trong giải pháp này.

PCNetwork.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface PCNetwork : NSObject
+ (NSString *)getIPAddress; // Prefers IPv4
+ (NSString *)getIPAddress:(BOOL)preferIPv4;
+ (NSDictionary *)getIPAddresses;
@end

NS_ASSUME_NONNULL_END

PCNetwork.m

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

#define IP_UNKNOWN          @"0.0.0.0"
#define IP_ADDR_IPv4        @"ipv4"
#define IP_ADDR_IPv6        @"ipv6"

@implementation PCNetwork
#pragma mark - IP
+ (NSString *)getIPAddress {
    return [self getIPAddress:YES];
}

+ (NSString *)getIPAddress:(BOOL)preferIPv4 {
    NSArray *searchArray = [self getAllIFSearchArray:preferIPv4];
    NSDictionary *addresses = [self getIPAddresses];
    DLog(@"addresses: %@", addresses);

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

+ (NSDictionary *)getIPAddresses {
    NSMutableDictionary *addresses = [NSMutableDictionary dictionary];
    struct ifaddrs *interfaces;
    BOOL success = !getifaddrs(&interfaces); // Retrieve the current interfaces : returns 0 on success
    if (success) {
        struct ifaddrs *temp_interface;
        for (temp_interface = interfaces; temp_interface; temp_interface = temp_interface->ifa_next) { // Loop through linked list of interfaces
            if (!(temp_interface->ifa_flags & IFF_UP) || (temp_interface->ifa_flags & IFF_LOOPBACK)) { // Ignore interfaces that aren't up and loopback interfaces.
                continue;
            }

            if (!temp_interface->ifa_addr) {
                continue;
            }

            const struct sockaddr_in *temp_addr = (const struct sockaddr_in*)temp_interface->ifa_addr;
            if (temp_addr->sin_family == AF_INET || temp_addr->sin_family == AF_INET6) {
                char addrBuf[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
                NSString *name = [NSString stringWithUTF8String:temp_interface->ifa_name];
                NSString *type = nil;
                if (temp_addr->sin_family == AF_INET) {
                    if (inet_ntop(AF_INET, &temp_addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
                        type = IP_ADDR_IPv4;
                    }
                } else {
                    const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)temp_interface->ifa_addr; // AF_INET6
                    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];
                }
            }
        }
        freeifaddrs(interfaces); // Free memory
    }
    return addresses.count ? addresses.copy : nil;
}

#pragma mark - Inter Frame Spacing
+ (NSArray *)getAllIFSearchArray:(BOOL)preferIPv4 {
    NSArray *KNOWN_WIFI_IFS = @[@"en0"];
    NSArray *KNOWN_WIRED_IFS = @[@"en1",@"en2",@"en3",@"en4"];
    NSArray *KNOWN_CELL_IFS = @[@"pdp_ip0",@"pdp_ip1",@"pdp_ip2",@"pdp_ip3"];

    NSMutableArray *searchArray = [NSMutableArray array];

    // Add wifi
    [searchArray addObjectsFromArray:[self getIFSearchArrayWith:KNOWN_WIFI_IFS preferIPv4:preferIPv4]];

    // Add cell
    [searchArray addObjectsFromArray:[self getIFSearchArrayWith:KNOWN_CELL_IFS preferIPv4:preferIPv4]];

    // Add wired
    [searchArray addObjectsFromArray:[self getIFSearchArrayWith:KNOWN_WIRED_IFS preferIPv4:preferIPv4]];

    return searchArray.copy;
}

+ (NSArray *)getIFSearchArrayWith:(NSArray *)iFList preferIPv4:(BOOL)preferIPv4 {
    NSMutableArray *searchArray = [NSMutableArray array];
    for (NSString *iFType in iFList) {
        if (preferIPv4) {
            [searchArray addObject:[NSString stringWithFormat:@"%@/%@", iFType, IP_ADDR_IPv4]];
            [searchArray addObject:[NSString stringWithFormat:@"%@/%@", iFType, IP_ADDR_IPv6]];
        } else {
            [searchArray addObject:[NSString stringWithFormat:@"%@/%@", iFType, IP_ADDR_IPv6]];
            [searchArray addObject:[NSString stringWithFormat:@"%@/%@", iFType, IP_ADDR_IPv4]];
        }
    }
    return searchArray.copy;
}

@end

0

trong iOS 13.4.1 không hoạt động với tôi. tôi dùng cái này để sửa nó

+ (NSString *)getIPAddress{
    NSArray *searchArray =
    @[ IOS_VPN @"/" IP_ADDR_IPv4, IOS_VPN @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_4_3G @"/" IP_ADDR_IPv4, IOS_4_3G @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6];

    __block NSDictionary *addresses = [self getIPAddressArray];

    __block NSString *address;
    [searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
    {
         address = addresses[key];
         if ([key rangeOfString:@"ipv6"].length > 0  && ![[NSString stringWithFormat:@"%@",addresses[key]] hasPrefix:@"(null)"] ) {
             if ( ![addresses[key] hasPrefix:@"fe80"]) {
                 //     isIpv6 = YES;
                 *stop = YES;
              }
         }else{
             if([self isValidatIP:address]) {
                 *stop = YES;
             }
         }
     } ];
    return address ? address : @"error";
}
+ (NSString *)getIPType{
    NSString *ipAddress = [self getIPAddress];
    if ([self isValidatIP:ipAddress]) {
        return @"04";//ipv4
    }else{
        return @"06";//ipv6
    }
}
+ (NSDictionary *)getIPAddressArray{
    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;
}
+ (BOOL)isValidatIP:(NSString *)ipAddress {
    if (ipAddress.length == 0) {
        return NO;
    }
    NSString *urlRegEx = @"^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";

    NSError *error;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:urlRegEx options:0 error:&error];

    if (regex != nil) {
        NSTextCheckingResult *firstMatch=[regex firstMatchInString:ipAddress options:0 range:NSMakeRange(0, [ipAddress length])];

        if (firstMatch) {
            NSRange resultRange = [firstMatch rangeAtIndex:0];
            NSString *result=[ipAddress substringWithRange:resultRange];
            //输出结果
            NSLog(@"%@",result);
            return YES;
        }
    }
    return NO;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.