IOS获取IP地址详解

上书提到小编自制Demo中引进了一个新的技术--获取IP地址(源码来源于网上),由此本回将对于上文提及的代码做以详尽的分析,如果有更好的理解方式或者建议,可在评论区或私聊与小编进行探讨~更好的文章可以投稿专题 更多内容收录专题

https://www.jianshu.com/c/ec6bd31fdbe1

源码
//Get IP Address
- (NSString *)getIPAddress{
    NSString *adress = @"error"; 
    struct ifaddrs *interfaces = NULL; 
    struct ifaddrs *temp_addr = NULL; 
    int success = 0; 

    success = getifaddrs(&interfaces); 
    if (success == 0) {
        temp_addr = interfaces; //将结构体复制给副本temp_addr
        while (temp_addr != NULL) {
          
            if (temp_addr->ifa_addr->sa_family == AF_INET) {
                //check if interface is en0 which is the wifi conection on the iphone
                if ([[NSString stringWithUTF8String:temp_addr ->ifa_name] isEqualToString:@"en0"]) {
                    adress = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];

                }
            }
            temp_addr = temp_addr -> ifa_next;
        }
    }
    
    //Free memory
    //extern void freeifaddrs(struct ifaddrs *);
    freeifaddrs(interfaces);
    return (adress);
}

重点强调:在实现获取IP地址代码的最初工作是先导入相关的头文件

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

分析:

  • (NSString *)getIPAddress{......}
因为我们的目的是将IP地址返回后并且传送给UILabel标签从而改变Label.text的内容(参考上一章节的UILabel *getIPLabel)
所以,返回值设置为NSString类型,返回字符串类型,并由标签接收
  • NSString *adress = @"error";
我们先设置一个目标参考量,利用字符串类型的对象adress来作为IP地址的返回值,其中我们先设定返回地址为error,或者你可以自己定义NSString *adress = @“error of the IP address and I can return it.”;
  • struct ifaddrs *interfaces = NULL; struct ifaddrs *temp_addr = NULL;
    在这两部分我们设置了两个结构体变量interfaces and temp_addr为NULL;
我们先来看一下这两个C结构体的本体
struct ifaddrs{
  struct ifaddrs *ifa_next; //Next item in list
  char * ifa_name; //Name of interface
  unsigned int ifa_flags; //Flags from SIOCGIFFLAGS
  struct sockaddr * ifa_addr;  //Address if interface
  struct sockaddr * ifa_netmask; //Netmask of interface
  union{
      struct sockaddr * ifu_broadaddr; //Broadcast address of interface
      struct sockaddr * ifu_dstaddr; //point-to-point destination address
  }ifa_ifu;
  #define ifa_broadaddr ifa_ifu.ifu_broadaddr
  #define ifa_dstaddr if_a_ifu.ifu_dstaddr
  void *ifa_data; //Address-specific data
};
讲解部分

1.ifa_next:指向链表的下一个成员;【链表:链表是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的】
2.ifa_name: 是接口的名称,以0结尾的字符串,比如eth0,10;
3.ifa_flags: 是接口的标识符;
4.ifa_netmask: 存储该接口的子网掩码;
5.结构体变量存储广播地址或者对点地址
6.ifa_data: 存储了该接口协议族的特殊信息,通常是NULL;

深度剖析

ifa_name

#define IOS_CELLULAR @"pdop_ip0":
#define IOS_WIFI @"en0";
#define IOS_VPN @"utun0";
#define IP_ADDR_IPV4 @"ipv4";
#define IP_ADDR_IPV6 @"ipv6";
因此,在内部判断时候将字符串以UTF8格式读取,temp_addr-->interfaces,利用结构体指针的的指向运算符指向结构体中的ifa_name,来获取接口的名称(WIFI链接状态下),如果WIFI断开则失效,返回值为false。
同样,使用上述宏定义可以判断不同设备状态下获取的地址
 if ([[NSString stringWithUTF8String:temp_addr ->ifa_name] isEqualToString:@"en0"]) {
                    adress = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];

                }

观察代码不难分析出, [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)]将返回的值赋给了adress,可inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr是什么意思,我们继续来看,先返回success部分讲解。

getifaddrs()函数
int success = 0; 

    success = getifaddrs(&interfaces); 
在这里我们定义了一个整形变量success并给了初始化的数值为0,下一行调用了getifaddrs()函数,并且将返回值赋予了success。
定义:函数getifaddrs(int getifaddrs(struct ifaddrs **_ifap))
作用:获取本地网络接口信息,将存储在链表中,链表头结点指针存储于_ifap中带回,函数执行成功返回值为0,失败返回值为-1

此时,如果getifaddrs()成功的获取了本地网络接口的信息时候,则success被赋予0

if (temp_addr->ifa_addr->sa_family == AF_INET) {......}
当中的temp_addr是结构体ifaddrs指针,利用指针运算符取出成员变量ifa_addr结构体(定义如下代码)来获取网络的接口
struct sockaddr{
    _uint8_t sa_len; //total length
    sa_family_t sa_family; //address family
    char sa_data[14]; //actually larger
    #define AF_INET 2  
}
其中,面向网络的套接字的家族名为 AF_INET,其地址是主机--端口
例如:套接字-->电话接口 那么 主机或端口-->号码

重头戏 “sockaddr_in”

[NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];

此处,我们将temp_addr强制转换为结构体sockaddr_in类型,这个是套接字的返回类型,使得temp_addr指向ifa_addr的sin_addr

   struct sockaddr_in {
                       __uint8_t    sin_len;
                        sa_family_t    sin_family;
                       in_port_t    sin_port;
                        struct    in_addr sin_addr;
                       char        sin_zero[8];
                   };
而结构体 in_addr sin_addr将返回地址给adress

重要部分完成后,继续传递下一个链表知道范围为空链表为止

 temp_addr = temp_addr -> ifa_next;

最后,切记将指针所分配的内存释放掉

//Free memory
    //extern void freeifaddrs(struct ifaddrs *);
    freeifaddrs(interfaces);
好啦,今天的文章到这也就结束,小编通过各人搜集,将各个部分整理在了一起,希望能为大家提供帮助哦,从下一回开始,我们继续空间选择器的介绍以及更多Demo的引入,谢谢大家支持!!!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 代码中到处都需要命名。作为程序员,我们得给类命名,给变量命名,给函数命名,给参数命名,给命名空间命名,等等等等。下...
    流哲羽阅读 1,716评论 1 5
  • 今天是周日(大寒) 起床:6:45 就寝:22:30 天气:大雨 心情:一般 纪念日:无 任务清单 昨日完成的任务...
    三七木木阅读 92评论 0 0
  • “你怎么那么喜欢看《悲惨世界》?”朋友笑道。“《悲惨世界》其实并不悲惨!”我脱口而出。对于己所言,我自己也半信半疑...
    笑若叶子阅读 428评论 0 3
  • 朔方雪舞天地中, 喜把春秋当作冬。 银装层层尽呵护, 可怜南国无雪拥。
    点点可可阅读 304评论 0 4