Bagaimana cara mendapatkan alamat IP saya secara terprogram di iOS / macOS?

124

Saya ingin mendapatkan alamat IP iPad saya secara terprogram. Bagaimana cara menanyakan subsistem jaringan untuk mengetahui alamat IPv4 (dan IPv6) saya?

PS: Bisakah saya menonaktifkan IPv6?

Wilbur
sumber
14
Sehubungan dengan 'PS' Anda di atas, harap jangan nonaktifkan IPv6 secara terprogram di perangkat seseorang. Itu hanya kasar.
Jeremy Visser
Anda tidak dapat menonaktifkan IPv6. Itu wajib. Faktanya, aplikasi iOS Anda harus mendukung IPv6.
Michael Hampton

Jawaban:

132

Kode berikut menemukan semua alamat IPv4 dan IPv6 di perangkat iOS atau OSX. getIPAddressMetode pertama bertindak kurang lebih seperti kode yang lebih lama dalam jawaban ini: Anda dapat memilih salah satu atau jenis alamat lainnya, dan selalu lebih suka WIFI daripada seluler (jelas Anda dapat mengubahnya).

Lebih menariknya, ia dapat mengembalikan kamus dari semua alamat yang ditemukan, melewatkan alamat untuk not upantarmuka, atau alamat yang terkait dengannya loopback. Kode sebelumnya serta solusi lain tentang topik ini tidak akan mendekode IPv6 dengan benar (inet_ntoa tidak dapat mengatasinya). Ini ditunjukkan kepada saya oleh Jens Alfke di forum Apple - fungsi yang tepat untuk digunakan adalah inet_ntop (lihat halaman manual, dan atau lihat artikel inet_ntop ini juga disediakan oleh Jens.

Kunci kamus memiliki bentuk "antarmuka" "/" "ipv4 atau 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: Kode diperbarui pada 16 Mei 2014 (bug ditunjukkan oleh lhunath, lihat komentar). Alamat loopback sekarang dikembalikan, tetapi mudah bagi Anda untuk menghapus komentar pada tes untuk mengecualikannya sendiri.

EDIT2: (oleh beberapa orang yang tidak dikenal): Ditingkatkan lebih lanjut 13 Maret 2015: Jika pengguna menggunakan VPN (terlepas dari WiFi atau Seluler), kode sebelumnya akan gagal. Sekarang, ini berfungsi bahkan dengan koneksi VPN. Koneksi VPN diutamakan daripada WiFi dan Sel karena begitulah cara perangkat menanganinya. Ini bahkan harus berfungsi untuk Mac karena koneksi VPN di Mac juga menggunakan IF utun0 tetapi tidak diuji.

EDIT3: (9/8/2016) Mengingat masalah yang dialami oleh @Qiulang (lihat komentar) dengan kode VPN (yang ditambahkan orang lain), saya telah mengomentarinya. Jika ada yang tahu pasti bagaimana menentukan pengguna VPN, silakan bergabung dengan komentar.

David H.
sumber
seberapa sering addrmendapatkan NULL? pengguna saya terkadang mendapatkan NULL. apakah Anda tahu apa kemungkinan alasannya?
HelmiB
Saya menggunakan hanya AF_INETuntuk memeriksa. bisa jadi ini?
HelmiB
1
Saya tidak tahu, tetapi yang pasti mungkin pengguna Anda dapat berada di jaringan IPV6.
David H
2
Ini harus menjadi jawaban yang "benar" karena dilengkapi dengan 3G. Terima kasih atas pembaruannya.
palme
1
Saya rasa ini tidak berfungsi dengan benar dengan IPv6. Fungsi inet_ntoa mengambil alamat IPv4, jadi dalam kasus di mana sa_type == AF_INET6, Anda mengambil alamat IPv6 dan mengetikkannya ke IPv4 dan mengubahnya menjadi string (pada dasarnya dari 32 bit tinggi 128-bit alamat.)
Jens Alfke
88

Dalam file implementasi Anda .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;

} 
Raptor
sumber
1
Tidak ada antarmuka en0 yang muncul di daftar antarmuka, jadi hanya mengembalikan kesalahan. Tidak yakin apakah ada yang berubah tetapi jelas tidak berfungsi untuk saya di iOS 5.x
wuf810
11
Jika Anda melihat di bawah, Anda akan menemukan beberapa kode yang sedikit dimodifikasi yang mengembalikan alamat sel (3G) jika WIFI dimatikan.
David H
1
Kode berikut memberikan alamat IPv4 en0 saja. Bagaimana dengan IPv6? Dapatkah seseorang memberikan contoh kode tentang cara mendapatkannya kembali?
Oded Regev
2
Yang ini tidak menemukan alamat ip ketika perangkat tidak terhubung ke wifi, tetapi ke jaringan
seluler
1
Ubah kondisi if di bawah if ([[NSString stringWithUTF8String: temp_addr-> ifa_name] isEqualToString: @ "en0"]) To if ([[NSString stringWithUTF8String: temp_addr-> ifa_name] isEqualToString: @ "en0"] || [[NSString stringWithUTF8String: temp_addr-> ifa_name] isEqualToString: @ "en0"] || stringWithUTF8String: temp_addr-> ifa_name] isEqualToString: @ "pdp_ip0"]) untuk mendapatkan IP saat perangkat tidak terhubung ke WIFI ..
Raju
4

Banyak solusi yang ada hanya mempertimbangkan antarmuka nirkabel, yang tidak akan berfungsi untuk koneksi kabel melalui adaptor Ethernet (mis. Tanpa Wifi atau 3G); lihat solusi yang lebih baru ini yang juga mempertimbangkan alamat IP yang diperoleh melalui antarmuka kabel.

iPad: Cara mendapatkan alamat IP secara terprogram WIRED (bukan melalui nirkabel)

lundhjem.dll
sumber
Luar biasa! Saya akan segera menambahkan ini ke kode saya di atas. Apa itu en1 - tahukah kamu?
David H
1
Saya tidak, tapi saya perhatikan itu bukan keluarga AF_INET seperti antarmuka en lainnya
lundhjem
1
@DavidH baru-baru ini IP ethernet telah muncul di en1 pada iPad tertentu, jadi tampaknya IP tersebut juga merupakan anggota keluarga AF_INET dalam skenario tertentu. Anda harus memeriksa antarmuka itu juga.
lundhjem
3

Dapatkan alamat IP menggunakan 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
}
BHAVIK PANCHAL
sumber
1

Solusi saat ini tidak mengembalikan perangkat en0 pada OS X, kode berikut menggunakan Kerangka Konfigurasi Sistem untuk mendapatkan antarmuka kemudian menggunakan fungsi C standar untuk mendapatkan alamat 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;
}
A. Badger
sumber
Sayangnya, SCDynamicStoreCreate tidak tersedia di iOS, seperti: SCDynamicStoreCopyValue
Alex Zavatone
1

Jawaban ini terinspirasi dari jawaban @ DavidH. Saya memperbaiki beberapa masalah, diganti inet_ntopdengan getnameinfoyang memungkinkan pendekatan yang lebih bersih. Perhatikan bahwa ini menghasilkan kamus yang memetakan nama antarmuka ke larik alamat IP (sebuah antarmuka dapat memiliki beberapa IPv4 dan IPv6 yang terkait dengannya, secara teknis). Itu tidak membedakan antara IPv4 dan 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;
lhunath
sumber
Saya belum pernah melihat strf- apakah itu fungsi pembantu yang Anda miliki dalam kode Anda?
David H
Ya, maaf, ada beberapa pembantu dalam kode di atas. errdan strf. Anda bisa mengganti strfdengan -stringWithCString:encoding:. erradalah NSLogpembungkus yang juga mengeluarkan file & baris. github.com/Lyndir/Pearl/blob/master/Pearl/...
lhunath
1

Jawaban @ DavidH berfungsi dengan baik sampai saya mendapatkan hasil ini dari beberapa jaringan seluler 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";
}

Saya tidak menggunakan vpn jadi saya tidak tahu mengapa saya punya utun0 / ipv6.

--- Diperbarui ---

Saya selanjutnya men-debug masalah ini dan menemukan bahwa saya bisa mendapatkan alamat vpn palsu bahkan di jaringan 4G lain (apakah ini bug 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"";
}

Jika saya menggunakan vpn, saya akan mendapatkan ini:

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

Jadi utun1 BUKAN utun0

Tanpa mencari tahu mengapa saya harus meninggalkan cek vpn :(

---- perbarui ----

Saya melaporkan bug (28131847) ke apple dan menjawab dengan "Tidak semua antarmuka utun untuk VPN. Ada fitur OS lain yang menggunakan antarmuka utun."

Tetapi ketika saya bertanya bagaimana cara mendapatkan alamat IP vpn yang valid, jawaban mereka agak kecewa, "Anda dapat masuk ke Pengaturan -> VPN dan melihat konfigurasi VPN Anda untuk melihat apakah VPN aktif. Dalam beberapa kasus, Anda dapat melihat menetapkan alamat IP di sana juga. Kami sekarang menutup laporan bug ini. " :(

---- perbarui 2016/11/04 ----

Saya mengalami masalah lagi dan saya perlu memodifikasi lebih lanjut jawaban @ DavidH untuk memperbaikinya:

Saya berada di jaringan 4G dan saya mendapat alamat ini:

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

Dengan jawaban aslinya saya akan mendapatkan wifi IP fe80 :: 8dd: 7d92: 4159: 170e, yang palsu dan koneksi gagal.

Jadi saya memodifikasi kodenya menjadi suka,

[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;
     }
 } ];
Qiulang
sumber
Saya memposting di forum internal tentang masalah VPN, insinyur Apple telah menawarkan bantuan. Saya ingin Anda menghubungi saya secara offline dhoerl di mac dot com
David H
1

Solusi hebat untuk cepat dalam file ini yang menyajikan semua detail.

Di salah satu aplikasi saya, saya perlu mengambil alamat IP wifi. Saya sudah menggunakan jawaban di atas, di swift 3 seperti ini:

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

Dalam kode ini, Ini macet karena saya harus memeriksa nildi setiap pernyataan yang saya gunakan sebagai opsional ?. Jadi lebih baik bagi saya untuk menggunakan file terkait yang diberikan di kelas saya. Menjadi mudah bagi saya untuk memeriksa sekarang seperti:

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
}

Saya telah mengurai string "."karena Kelas Antarmuka mengembalikan dua antarmuka di iPhone saya untuk en0alamat seperti "fb00 ::" dan alamat seperti "101.10.1.1"

Max
sumber
1
Tautan ke solusi potensial selalu diterima, tetapi harap tambahkan konteks di sekitar tautan sehingga sesama pengguna Anda akan tahu apa itu dan mengapa itu ada. Selalu mengutip bagian paling relevan dari sebuah tautan penting, jika situs target tidak dapat dijangkau atau offline secara permanen. Mempertimbangkan bahwa tidak lebih dari satu tautan ke situs eksternal adalah alasan yang mungkin untuk Mengapa dan bagaimana beberapa jawaban dihapus? .
Paul Roub
1
Ok akan memperbarui sesuai karena itu sangat membantu saya untuk pemahaman yang lebih baik
Max
1

Saya membuat file sederhana untuk mendapatkan alamat ip. Saya mendasarkan solusi ini pada jawaban @ lundhjem, @ DavidH dan @ Ihunath. Ini mempertimbangkan koneksi kabel. Saya belum memasukkan VPN dalam solusi ini.

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
cessmestreet
sumber
0

di iOS 13.4.1 tidak berfungsi untuk saya. saya menggunakan ini memperbaikinya.

+ (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;
}
yaoning
sumber