iPhone / iPad / OSX:如何以编程方式获取我的IP地址?
我想以编程方式获取我的iPad的IP地址。 如何查询networking子系统以查明我的IPv4(和IPv6)地址是什么?
谢谢。 PS:我可以以某种方式禁用IPv6吗?
以下代码可查找iOS或OSX设备上的所有IPv4和IPv6地址。 第一个getIPAddress
方法或多或less的作为这个答案中的旧代码:你可以更喜欢一个或其他types的地址,它总是喜欢WIFI蜂窝(显然你可以改变这一点)。
更有意思的是,它可以返回find的所有地址的字典,跳过接口的地址或者与loopback
相关的地址。 以前的代码以及有关此主题的其他解决scheme将不能正确解码IPv6(inet_ntoa无法处理它们)。 Jens Alfke在一个苹果论坛上指出了这一点 – 使用的正确函数是inet_ntop(查看手册页,或者参阅Jens提供的inet_ntop文章。
字典键具有“接口”“/”“ipv4或ipv6”的forms。
#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指出的bug,见评论)。 环回地址现在已经返回,但是您可以轻松地取消注释testing以自行排除它们。
编辑2:(由一些未知的人):进一步改进2015年3月13日:万一用户使用VPN(无论通过WiFi或蜂窝),以前的代码将失败。 现在,甚至在VPN连接的情况下也能工作。 VPN连接优先于WiFi和Cell,因为这是设备处理它的方式。 这应该甚至适用于Mac,因为Mac上的VPN连接也使用IF utun0,但未经过testing。
编辑3:(9/8/2016)鉴于@Qiulang(见评论)与VPN代码(别人添加)所经历的问题,我已经评论了。 如果有人确切地知道如何指定一个用户的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; }
只要拨打其中一个IP地址查找服务,很容易吗? 我已经build立了http://ipof.in作为服务,以JSON / XML或纯文本forms返回设备的IP地址。 你可以在这里find它们
如果您需要HTTPS,则可以使用带有https
前缀的相同URL。 好处是,即使你在一个无线上网,你会得到公共地址。
许多现有的解决scheme只考虑无线接口,这将无法通过以太网适配器进行有线连接(即没有Wifi或3G)。 请参阅这个考虑通过有线接口获得的IP地址的更近期的解决scheme。
iPad:如何以编程方式获取IP地址有线(不通过无线)
当前的解决scheme不会返回OS X上的en0设备,下面的代码使用系统configuration框架获取接口,然后使用标准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的答案启发的。 我修复了一些问题,用getnameinfo
replace了inet_ntop
,这样可以使用更清晰的方法。 请注意,这会产生一个将接口名称映射到一个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;
使用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 }
@ DavidH的答案很好,直到我从一些4G蜂窝networking得到这个结果:
{ "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。
– – 更新 – –
我进一步debugging这个问题,发现我可以得到一个虚假的VPN地址,即使在其他4Gnetworking(这是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),并回答说:“并不是所有的utun接口都是用于VPN的,还有其他一些使用utun接口的操作系统function。
但是当我问如何得到一个有效的VPN IP地址时,他们的回答是相当失望的,“你可以进入设置 – > VPN,看看你的VPNconfiguration,看看VPN是否激活,在某些情况下,你可以看到那里也分配了IP地址,现在我们正在closures这个错误报告。“ 🙁
—-更新2016/11/04
我再次遇到这个问题,我需要进一步修改@ DavidH的解决scheme:
我在4Gnetworking,我得到这个地址:
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; } } ];
伟大的解决scheme,迅速在这个文件 ,提供所有的细节。
在我的一个应用程序中,我需要获取wifi的IP地址。 我已经在上面用了答案,就像这样:
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 }
我已经parsing了string"."
因为接口类返回两个接口在我的iPhone en0
地址,如“fb00 ::”和地址如“101.10.1.1”