上书提到小编自制Demo中引进了一个新的技术--获取IP地址(源码来源于网上),由此本回将对于上文提及的代码做以详尽的分析,如果有更好的理解方式或者建议,可在评论区或私聊与小编进行探讨~
源码
//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);
网友评论