iOS开发之网络监听(一)Reachability
demo下载
Reachability对系统的网络状况类SCNetworkReachability进行的封装,持有全局的网络状况句柄reachabilityRef,简化了SCNetworkReachability的Api以及网络状态,使开发者使用起来更加简单。
总而言之,这是一个用来检测网络状态的一个三方类,功能类似于AF的网络管理类AFNetworkReachabilityManager。
1、简单使用
#import "ViewController.h"
#import "Reachability.h"@interface ViewController ()//网络管理类,全局的
@property (nonatomic, strong) Reachability *reachability;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib.//初始化self.reachability = [Reachability reachabilityForInternetConnection];//当前网络状态NSLog(@"当前网络状态:%ld",(long)[self.reachability currentReachabilityStatus]);NSLog(@"%@", self.reachability.currentReachabilityString);//开启网络状态变化的监听,开启网络监听[self.reachability startNotifier];//断网回调self.reachability.unreachableBlock = ^(Reachability *reachability) {//注意这里的回调是在在子线程中NSLog(@"子线程 : %@",[NSThread currentThread]);dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"无网了!");});};//来网回调self.reachability.reachableBlock = ^(Reachability *reachability) {NSLog(@"子线程 :%@",[NSThread currentThread]);dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"有网了!");switch (reachability.currentReachabilityStatus) {case ReachableViaWiFi:break;case ReachableViaWWAN:break;default:break;}});};
}- (void)dealloc {[self.reachability stopNotifier];
}@end
不开启网络状态监听,可以直接get到网络状态等。开启了,网络状态改变会回调。
回调是在子线程中回调的,通知是在主线程,可以看看源码回调方法的实现:
-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags
2、Reachability内部实现逻辑
1、创建Reachability对象
+(Reachability *)reachabilityForInternetConnection
{ struct sockaddr_in zeroAddress;bzero(&zeroAddress, sizeof(zeroAddress));zeroAddress.sin_len = sizeof(zeroAddress);zeroAddress.sin_family = AF_INET;return [self reachabilityWithAddress:&zeroAddress];
}
在这个方法中创建了sockaddr_in类型的zeroAddress,即hostName,并设置了其sin_len和sin_family。
+(Reachability *)reachabilityWithAddress:(void *)hostAddress
{SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);if (ref) {id reachability = [[self alloc] initWithReachabilityRef:ref];return reachability;}return nil;
}
将第一步创建的zeroAddress传入这个方法中,调用系统SCNetworkReachability类中SCNetworkReachabilityRef的创建函数,传入zeroAddress等参数,创建了ref。
-(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref
{self = [super init];if (self != nil) {self.reachableOnWWAN = YES;self.reachabilityRef = ref;// We need to create a serial queue.// We allocate this once for the lifetime of the notifier.self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL);}return self;
}
继而将创建的ref传入最终的init方法中,将ref保存为全局的变量reachabilityRef,顺便创建了一个串行队列,暂时不知道其作用。
2、获取网络状态
对象创建完后,就可以获取网络状态了。
-(NetworkStatus)currentReachabilityStatus
{if([self isReachable]){if([self isReachableViaWiFi])return ReachableViaWiFi;#if TARGET_OS_IPHONEreturn ReachableViaWWAN;
#endif}return NotReachable;
}
这里先调用了isReachable方法
-(BOOL)isReachable
{SCNetworkReachabilityFlags flags; if(!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))return NO;return [self isReachableWithFlags:flags];
}
这个方法中调用系统类SCNetworkReachability中的SCNetworkReachabilityGetFlags函数,获取了系统的网络状态的flags。
取到flags后,调用了如下方法
-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags
{BOOL connectionUP = YES;if(!(flags & kSCNetworkReachabilityFlagsReachable))connectionUP = NO;if( (flags & testcase) == testcase )connectionUP = NO;#if TARGET_OS_IPHONEif(flags & kSCNetworkReachabilityFlagsIsWWAN){// We're on 3G.if(!self.reachableOnWWAN){// We don't want to connect when on 3G.connectionUP = NO;}}
#endifreturn connectionUP;
}
取到系统的flags后其实网络状态已经知道了,再用这个方法的目的是将系统复杂的flags简单化,简化成,有网络和无网络。这个方法只判断有网络还是无网络。
返回到-(NetworkStatus)currentReachabilityStatus方法,如果无网络,直接返回NotReachable,如果有,则又调用了如下方法判断是不是WIFI网络,如果是返回ReachableViaWiFi不是直接返回ReachableViaWWAN。
-(BOOL)isReachableViaWiFi
{SCNetworkReachabilityFlags flags = 0;if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)){// Check we're reachableif((flags & kSCNetworkReachabilityFlagsReachable)){
#if TARGET_OS_IPHONE// Check we're NOT on WWANif((flags & kSCNetworkReachabilityFlagsIsWWAN)){return NO;}
#endifreturn YES;}}return NO;
}
整个流程一气呵成,思维缜密,没找出一点漏洞,感觉很完美。
3、开启网络监听
-(BOOL)startNotifier
{// allow start notifier to be called multiple timesif(self.reachabilityObject && (self.reachabilityObject == self)){return YES;}SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL };context.info = (__bridge void *)self;if(SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context)){// Set it as our reachability queue, which will retain the queueif(SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue)){// this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves// woahself.reachabilityObject = self;return YES;}else{
#ifdef DEBUGNSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError()));
#endif// UH OH - FAILURE - stop any callbacks!SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);}}else{
#ifdef DEBUGNSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError()));
#endif}// if we get here we fail at the internetself.reachabilityObject = nil;return NO;
}
通过SCNetworkReachabilitySetCallback(,)函数注册了回调,实现这个函数当网络状态改变时回会执行响应的回调方法。这个函数中第一个参数及时初始化的时候创建ReachabilitySCNetworkReachabilityRef对象reachabilityRef。第二个参数传入了一个函数SCNetworkReachabilityCallBack类型的函数。
然后通过函数SCNetworkReachabilitySetDispatchQueue(,)将自己初始化类的时候创建的队列和reachabilityRef传入,在系统类的SCNetworkReachability里面应该是注册了回调以及回调的方法。
如果注册成功了,self.reachabilityObject = self,这样就可以理解这个方法刚开始的判断的目的了。完美的防止了重复多次的startNotifier。
当网络状态改变时,会通过TMReachabilityCallback这个函数回调,在这个函数里
static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
{
#pragma unused (target)Reachability *reachability = ((__bridge Reachability*)info);// We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool,// but what the heck eh?@autoreleasepool {[reachability reachabilityChanged:flags];}
}
调用了判断网络状态的方法,然后进行了回调和通知的发送
-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags
{if([self isReachableWithFlags:flags]){if(self.reachableBlock){self.reachableBlock(self);}}else{if(self.unreachableBlock){self.unreachableBlock(self);}}// this makes sure the change notification happens on the MAIN THREADdispatch_async(dispatch_get_main_queue(), ^{[[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification object:self];});
}
可以看到通知是在主线程发送的,回调是在子线程中。
3、Reachability.h
1、一个通知
.h
extern NSString *const kReachabilityChangedNotification;
.m
NSString *const kReachabilityChangedNotification = @"kReachabilityChangedNotification";
网络状态变化通知名
2、一个枚举
typedef NS_ENUM(NSInteger, NetworkStatus) {// Apple NetworkStatus Compatible Names.NotReachable = 0,ReachableViaWiFi = 2,ReachableViaWWAN = 1
};
网络状况的枚举
3、两个回调
typedef void (^NetworkReachable)(Reachability * reachability);
typedef void (^NetworkUnreachable)(Reachability * reachability);
网络状况回调
4、构造方法
5、开启和关闭监听的方法
-(BOOL)startNotifier;
-(void)stopNotifier;
6、其他方法
其他一些get方法,获取当前网络状态,获取网络是否可达等。
4、构造方法
+(Reachability *)reachabilityForInternetConnection
+(Reachability *)reachabilityForInternetConnection
{ //定义了一个类型为sockaddr_in的结构体变量zeroAddressstruct sockaddr_in zeroAddress; //bzero()函数传入了两个参数 bzero(&zeroAddress, sizeof(zeroAddress));//指定了zeroAddress变量的sin_len的值,即zeroAddress的字节大小zeroAddress.sin_len = sizeof(zeroAddress);//制定了zeroAddress变量的地址族zeroAddress.sin_family = AF_INET;//以上几步是创建了一个IP地址的变量zeroAddressreturn [self reachabilityWithAddress:&zeroAddress];
}
sockaddr_in
struct sockaddr_in {__uint8_t sin_len; // 这是定义了一个__uint8_t类型的sin_lensa_family_t sin_family; // 地址族in_port_t sin_port; // 16位的TCP/UDP端口号struct in_addr sin_addr; // 32位的IPV4地址char sin_zero[8]; //
};
bzero
baero(),这个函数传入了两个参数,&zeroAddress 和 sizeof(zeroAddress),zeroAddress的地址和长度。
AF_INET
这个宏定义:#define AF_INET 2 /* internetwork: UDP, TCP, etc. */
+(Reachability *)reachabilityWithAddress:(void *)hostAddress
+(Reachability *)reachabilityWithAddress:(void *)hostAddress
{SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);if (ref) {id reachability = [[self alloc] initWithReachabilityRef:ref];return reachability;}return nil;
}
创建网络句柄ref,传入参数CFAllocatorRef类型的参数1,const struct sockaddr类型的参数2。
SCNetworkReachabilityCreateWithAddress是定义在SCNetworkReachability类中的一个函数,使用该函数传入struct sockaddr_in类型的域名地址和CFAllocatorRef类型的参数(叫个什么名字好呢),快捷的创建出一个网络句柄对象SCNetworkReachabilityRef
CFAllocatorRef在CoreFoundation框架中。
void *
不确定类型指针,OC中的id?
SCNetworkReachabilityRef
SCNetworkReachabilityRef 在 SystemConfiguration/SystemConfiguration.h 框架中,定义在SCNetworkReachability类中,是网络地址名称的句柄。
定义:
typedef const struct CF_BRIDGED_TYPE(id) __SCNetworkReachability * SCNetworkReachabilityRef;
-(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref
-(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref
{self = [super init];if (self != nil) {self.reachableOnWWAN = YES;self.reachabilityRef = ref;// We need to create a serial queue.// We allocate this once for the lifetime of the notifier.self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL);}return self;
}
这个就是Reachability的初始化方法了。
从上一个方法中传入网络句柄ref,设置成了全局的属性,创建了一个全局的串行队列。
二、SCNetworkReachability
1、作用:
The SCNetworkReachability API allows an application todetermine the status of a system's current networkconfiguration and the reachability of a target host.In addition, reachability can be monitored with notificationsthat are sent when the status has changed.
简单的来说SCNetworkReachability提供的Api可以获取目标域名下,手机系统的网络状况和配置信息,甚至可以对网络状态的变化进行监听。
2、Api:
1、SCNetworkReachabilityFlags
定义了网络状态的OPTIONS
2、SCNetworkReachabilityRef
This is the handle to a network address or name.
3、SCNetworkReachabilityCallBack
typedef void (*SCNetworkReachabilityCallBack) (SCNetworkReachabilityRef target,SCNetworkReachabilityFlags flags,void * __nullable info);
SCNetworkReachabilityCallBack 网络状地址网络可达性改变回调函数,回调三个参数,一个网络句柄SCNetworkReachabilityRef类型的target,网络目标对象,一个网络状况Option,一个不确定类型的info。
通过这个回调将对应网络域名的网络状况进行了回调,并可以携带其他信息info。
4、SCNetworkReachabilityCreateWithAddress
SCNetworkReachabilityRef __nullable
SCNetworkReachabilityCreateWithAddress (CFAllocatorRef __nullable allocator,const struct sockaddr *address) API_AVAILABLE(macos(10.3), ios(2.0));
这个函数提供了一个创建指定网络地址引用对象的函数。
传入参数allocator,CFAllocatorRef是什么?
sockaddr,网络域名的结构体变量。
返回SCNetworkReachabilityRef类型的网络句柄对象,也是一个结构体。
5、SCNetworkReachabilityGetFlags
Boolean
SCNetworkReachabilityGetFlags (SCNetworkReachabilityRef target,SCNetworkReachabilityFlags *flags) API_AVAILABLE(macos(10.3), ios(2.0));
从函数体上看传入falg和对应的网络句柄对对象,判断当前网络句柄下该网络状态是否可达。
6、其他
…
思考
句柄是什么?
句柄就是综合一个聚合相关功能的结构体?
网络句柄SCNetworkReachabilityRef,里面聚合了SCNetworkReachability对象的结构体,SCNetworkReachability本身也是一个结构体。对象的本质就是一个结构体。
句柄是什么呢?只可意会,不可言传。
iOS开发之网络监听(一)Reachability
iOS开发之网络监听(二)SCNetworkReachability
iOS开发之网络监听(一)Reachability相关推荐
- iOS开发笔记 - 网络篇
计算机网络基础 计算机网络是多台独立自主的计算机互联而成的系统的总称,最初建立计算机网络的目的是实现信息传递和资源共享. 如果说计算机是第二次世界大战的产物,那么计算机网络则是美苏冷战的产物. ...
- iOS开发之网络音乐播放器(SC音乐)(二)
iOS开发之网络音乐播放器(SC音乐)(二) 前言 iOS开发之网络音乐播放器(SC音乐)(一)已经介绍完播放控制.音乐数据获取解析.歌词显示等.本文在上文的基础上介绍锁屏播放设置,后台播放设置,手势 ...
- iOS开发系列--网络开发(转)
iOS开发系列--网络开发 2014-10-22 08:34 by KenshinCui, 66365 阅读, 56 评论, 收藏, 编辑 概览 大部分应用程序都或多或少会牵扯到网络开发,例如说新浪微 ...
- 【网络与系统安全实验】网络监听及防御技术
网络监听及防御技术 网络监听概述 基础知识 网络监听的概念 网络监听技术又叫做网络嗅探技术,顾名思义这是一种在他方未察觉的情况下捕获其通信报文或通信内容的技术. 在网络安全领域,网络监听技术对于网络攻 ...
- Android Glide加载图片、网络监听、设置资源监听
Glide加载图片.加载进度监听 前言 正文 一.项目配置 二.显示网络图片 三.添加设置资源监听 四.添加设置资源监听 五.添加加载进度条 六.封装工具类 七.源码 总结 前言 在日常开发中使用 ...
- 隔墙有耳 Linux系统下的网络监听技术(转)
前言:在网络中,当信息进行传播的时候,可以利用工具,将网络接口设置在监听的模式,便可将网络中正在传播的信息截获或者捕获到,从而进行攻击.网络监听在网络中的任何一个位置模式下都可实施进行.而黑客一般都是 ...
- 8月第1周安全回顾 0Day漏洞成企业最大威胁 应重视网络监听
文章同时发表在:[url]http://netsecurity.51cto.com/art/200708/52822.htm[/url] 本周(0730至0805)安全方面值得关注的新闻集中在安全管理 ...
- iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载
前言:iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载是在前篇iOS开发之网络编程--使用NSURLConnection实现大文件下载的基础上进行 断点续传的设置 ...
- chromedp网络监听_动态爬虫三:监听网络事件 + 监听js事件
一: 概述 上两篇文章介绍了cdp协议和chromedp库,从这篇文章开始动手实战一下,我们要拿到页面上更多的网络请求,最直接的想法就是类似于开发者工具里的network,只有一有网络请求就显示在列表 ...
最新文章
- python -- numpy 基本数据类型,算术运算,组合,分割 函数
- JHipster生成微服务架构的应用栈(三)- 业务微服务示例
- HDFS修改副本数并生效
- RBAC模型:表设计分析
- Parse a document from a String
- package.json mysql_package.json入门
- go语言服务器连接mysql,服务器mysql怎么配置才能远程连接
- eas软标签_商品防盗尤为重视,防盗软标签突显本质优势
- 关于Ibatis中的executeForObject方法使用时,需要考虑空的问题!
- 闪烁指示灯监控方案_机房温湿度监控检测方案【斯必得智慧机房】
- 这款 Android 图片选择库美哭了
- 并发容器Map之一:ConcurrentHashMap原理(jdk1.8)
- tomcat与mysql分离部署_apache+tomcat+mysql 实现动静分离
- Layui 表格渲染
- EMOS邮件系统安装(光盘安装版)
- 基于web的小区物业管理系统
- steam安裝位置linux,「Linux」- 安装 Steam 客户端 @20210219
- 理查德·克莱德曼钢琴曲全集(梦中的婚礼)
- 只用十行 Python 代码就提取了韦小宝的身份证信息
- 递推练习之费解的开关
热门文章
- “恐怖”的阿里一面,我究竟想问什么
- 计算机excel柱状图刻度单位,如何设置excel图表的坐标刻度和单位-excel 柱状图 坐标 区间刻度...
- 计算机网络有哪几种拓补结构,常见的五种计算机网络拓扑结构分析
- 神经网络Neural Networks概述
- LeetCode题解目录
- Python如何输出当前时间,时分秒,以及ms
- 我为什么不挣钱也要写公众号
- ios 图片简单360度旋转动画
- 联想升级Win11后触摸板失灵怎么办?
- ios 纯代码怎么适配ipad_iOS屏幕适配(纯代码)