一、整体介绍

  • 前面已经介绍了网络访问的NSURLSessionNSURLConnection,还有网页加载有关的webview,基本满足通常的网络相关的开发。

    其实在网络开发中还有比较常用的就是网络状态的检测。苹果对需要联网的应用要求很高,就是必须要进行联网检查。另外,当网络发生异常时能够及时提示用户网络已断开,而不是程序问题造成卡顿;当用户观看视频或下载大文件时,提示用户当前的网络状态为移动流量或wifi下,是否继续使用,以避免在用户不知情下产生过多流量资费等等。

  • 网络状态的检测有多种方法,常用的有三种

二、苹果Reachability使用

使用非常简单,将Reachability.hReachability.m加入项目中,在要使用的地方包含Reachability.h头文件,示例代码:

#import "Reachability.h"

/// 在刚开始就开始监听
- (void)viewDidLoad {
[super viewDidLoad];
// Reachability使用了通知,当网络状态发生变化时发送通知kReachabilityChangedNotification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(appReachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
// 检测指定服务器是否可达
NSString *remoteHostName = @"www.bing.com";
self.hostReachability = [Reachability reachabilityWithHostName:remoteHostName];
[self.hostReachability startNotifier];
// 检测默认路由是否可达
self.routerReachability = [Reachability reachabilityForInternetConnection];
[self.routerReachability startNotifier];
}
/// 当网络状态发生变化时调用
- (void)appReachabilityChanged:(NSNotification *)notification{
Reachability *reach = [notification object];
if([reach isKindOfClass:[Reachability class]]){
NetworkStatus status = [reach currentReachabilityStatus];
// 两种检测:路由与服务器是否可达 三种状态:手机流量联网、WiFi联网、没有联网
if (reach == self.routerReachability) {
if (status == NotReachable) {
NSLog(@"routerReachability NotReachable");
} else if (status == ReachableViaWiFi) {
NSLog(@"routerReachability ReachableViaWiFi");
} else if (status == ReachableViaWWAN) {
NSLog(@"routerReachability ReachableViaWWAN");
}
}
if (reach == self.hostReachability) {
NSLog(@"hostReachability");
if ([reach currentReachabilityStatus] == NotReachable) {
NSLog(@"hostReachability failed");
} else if (status == ReachableViaWiFi) {
NSLog(@"hostReachability ReachableViaWiFi");
} else if (status == ReachableViaWWAN) {
NSLog(@"hostReachability ReachableViaWWAN");
}
} }
}
/// 取消通知
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil];
}

代码中两种检测:默认路由是否可达、服务器是否可达。有很多人可能有疑问,检测是否联网就可以了,怎么还要检测是否服务器可达?默认路由可达?

其实虽然联网了,也不一定能访问外网(通常说的互联网)。比如连了一个路由器,但是路由器没有联网,那么也是不能联网的。还有就是网络数据包在网际层传递时,一个路由传到另一个路由称为一跳,当达到255跳(大部分路由设置为255)还没有传到目的地时,网络数据包则丢弃。

路由器有一套算法来保证路径最优,还有路由表(保存路径表),如果一个数据包在路由表中没有匹配的路径的话,那么路由器就将此数据包发送到默认路由,这里的默认路由就是上面检测的默认路由是否可达。(里面相当复杂,就此打住)

令人崩溃的是:Reachability并不能检测到服务器是否真的可达,只能检测设备是否连接到局域网,以及用的WiFi还是WWAN。即:把设备网络关了,立马检测出NotReachable,连接到路由器立马检测出是ReachableViaWiFi、、、

代码中使用了通知,则释放对象时一定要在dealloc中取消通知。我们知道,通知不能在进程间通信,在哪个线程发送通知则在哪个线程执行。如果想在其它线程监听,则在其它线程调用startNotifier函数,新开启的线程默认都没有开启runloop消息循环,因此还要开启runloop,如下:

	// 被通知函数运行的线程应该由startNotifier函数执行的线程决定
typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSString *remoteHostName = @"www.bing.com";
weakSelf.hostReachability = [Reachability reachabilityWithHostName:remoteHostName];
[weakSelf.hostReachability startNotifier]; weakSelf.routerReachability = [Reachability reachabilityForInternetConnection];
[weakSelf.routerReachability startNotifier];
// 开启当前线程消息循环
[[NSRunLoop currentRunLoop] run];
});

最后,如果想取消检测,调用stopNotifier方法即可

[self.hostReachability stopNotifier];
[self.routerReachability stopNotifier];

三、AFNetworkReachabilityManager使用

  • 直接使用

使用CocoaPods或者直接将AFNetwork下载并添加进项目。如果只是使用AFNetworkReachabilityManager而不适用其它网络功能则只将其.m和.h添加进项目即可。AFNetworkReachabilityManager使用了block的方式,当网络状态发生变化就会调用,且block的调用AFN已经将其限定在主线程下。下面介绍直接使用

#import "AFNetworkReachabilityManager.h"
- (void)afnReachabilityTest {
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
// 一共有四种状态
switch (status) {
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"AFNetworkReachability Not Reachable");
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"AFNetworkReachability Reachable via WWAN");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"AFNetworkReachability Reachable via WiFi");
break;
case AFNetworkReachabilityStatusUnknown:
default:
NSLog(@"AFNetworkReachability Unknown");
break;
}
}]; [[AFNetworkReachabilityManager sharedManager] startMonitoring];
}
  • 使用AFHTTPSessionManager

当使用AFN网络框架时,大多情况下,我们使用AFNetwork时会创建一个网络中间单例类,以防止换网络框架时要改动太多,比如替换之前用的多的ASI,如果有个中间类的话,替换就很简单,只需要修改中间类即可。使用时调用[NetworkTools sharedManager];即可

/// 头文件
#import "AFHTTPSessionManager.h" @interface NetworkTools : AFHTTPSessionManager
+ (instancetype)sharedManager;
@end --------------------------------------------------------------------------------- /// .m文件
#import "NetworkTools.h" @implementation NetworkTools
+ (instancetype)sharedManager {
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
//#warning 基地址
// instance = [[self alloc] initWithBaseURL:[NSURL URLWithString:@"http://www.bing.com"]];
instance = [[self alloc] init];
});
return instance;
}
- (instancetype)init {
if ((self = [super init])) {
// 设置超时时间,afn默认是60s
self.requestSerializer.timeoutInterval = 30;
// 响应格式添加text/plain
self.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/plain", nil]; // 监听网络状态,每当网络状态发生变化就会调用此block
[self.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusNotReachable: // 无连线
NSLog(@"AFNetworkReachability Not Reachable");
break;
case AFNetworkReachabilityStatusReachableViaWWAN: // 手机自带网络
NSLog(@"AFNetworkReachability Reachable via WWAN");
break;
case AFNetworkReachabilityStatusReachableViaWiFi: // WiFi
NSLog(@"AFNetworkReachability Reachable via WiFi");
break;
case AFNetworkReachabilityStatusUnknown: // 未知网络
default:
NSLog(@"AFNetworkReachability Unknown");
break;
}
}];
// 开始监听
[self.reachabilityManager startMonitoring];
}
return self;
}
@end

四、第三方框架使用

这个使用会更方便一点,有block和通知两种方式,且支持多线程,这里不再详细介绍,README.md有使用方法:

- (void)viewDidLoad {
[super viewDidLoad];
// Allocate a reachability object
Reachability* reach = [Reachability reachabilityWithHostname:@"www.bing.com"]; // Set the blocks
reach.reachableBlock = ^(Reachability*reach) {
// keep in mind this is called on a background thread
// and if you are updating the UI it needs to happen
// on the main thread, like this:
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"REACHABLE!");
});
}; reach.unreachableBlock = ^(Reachability*reach) {
NSLog(@"UNREACHABLE!");
}; // Start the notifier, which will cause the reachability object to retain itself!
[reach startNotifier];
}

问题解决

三种方式差不多,它们在检测设备是否连接局域网和连接方式时很灵敏,但是不能检测服务器是否可达。因为它们底层都是使用了SCNetworkReachabilitySCNetworkReachability发送网络数据包到服务器,但它并不会确认服务器真的收到了此数据包。所以,如果我们想确认是否服务器可达,则需要发送一个真实的网络请求。或者我们使用socket编程,建立一个tcp链接来检测(三次握手成功),只要链接成功则服务器可达。这样只会发送tcpip的报头,数据量最小。如果网络环境差,connect函数会阻塞,所以最后不要在主线程下,调用示例代码,示例如下:

#import <arpa/inet.h>

/// 服务器可达返回true
- (BOOL)socketReachabilityTest {
// 客户端 AF_INET:ipv4 SOCK_STREAM:TCP链接
int socketNumber = socket(AF_INET, SOCK_STREAM, 0);
// 配置服务器端套接字
struct sockaddr_in serverAddress;
// 设置服务器ipv4
serverAddress.sin_family = AF_INET;
// 百度的ip
serverAddress.sin_addr.s_addr = inet_addr("202.108.22.5");
// 设置端口号,HTTP默认80端口
serverAddress.sin_port = htons(80);
if (connect(socketNumber, (const struct sockaddr *)&serverAddress, sizeof(serverAddress)) == 0) {
close(socketNumber);
return true;
}
close(socketNumber);;
return false;
}

示例代码下载

在github下载

iOS网络4——Reachability检测网络状态的更多相关文章

  1. iOS中使用 Reachability 检测网络

    iOS中使用 Reachability 检测网络 内容提示:下提供离线模式(Evernote).那么你会使用到Reachability来实现网络检测.   写本文的目的 了解Reachability都 ...

  2. iOS开发网络篇—Reachability检测网络状态

    前言:当应用程序需要访问网络的时候,它首先应该检查设备的网络状态,确认设备的网络环境及连接情况,并针对这些情况提醒用户做出相应的处理.最好能监听设备的网络状态的改变,当设备网络状态连接.断开时,程序也 ...

  3. 【开发记录】iOS中使用 Reachability 检测网络

    如果你想在iOS程序中提供一仅在wifi网络下使用(Reeder),或者在没有网络状态下提供离线模式(Evernote).那么你会使用到Reachability来实现网络检测. 写本文的目的 了解Re ...

  4. iOS中使用 Reachability 检测网络区分手机网络类型 WiFi 和2 3 4 G

    如果你想在iOS程序中提供一仅在wifi网络下使用(Reeder),或者在没有网络状态下提供离线模式(Evernote).那么你会使用到Reachability来实现网络检测. 写本文的目的 了解Re ...

  5. [iOS 多线程 & 网络 - 2.8] - 检测网络状态

    A.说明 在网络应用中,需要对用户设备的网络状态进行实时监控,有两个目的:(1)让用户了解自己的网络状态,防止一些误会(比如怪应用无能)(2)根据用户的网络状态进行智能处理,节省用户流量,提高用户体验 ...

  6. 李洪强iOS开发之使用 Reachability 检测网络

    1.iOS平台是按照一直有网络连接的思路来设计的,开发者利用这一特点创造了很多优秀的第三方应用. 大多数的iOS应用都需要联网,甚至有些应用严重依赖网络,没有网络就无法正常工作. 2.在你的应用尝试通 ...

  7. iOS开发 - Swift实现检测网络连接状态及网络类型

    一.前言 在移动开发中,检测网络的连接状态尤其检测网络的类型尤为重要.本文将介绍在iOS开发中,如何使用Swift检测网络连接状态及网络类型(移动网络.Wifi). 二.如何实现 Reachabili ...

  8. ios开发利用AFN检测网络状态

    AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager]; [manager setRe ...

  9. iOS开发 利用Reachability判断网络环境

    导入头文件:#import "Reachability.h" 然后将 SystemConfiguration.framework 添加进工程: 1.检查当前的网络状态(wifi.W ...

随机推荐

  1. Java MyBatis 插入数据库返回主键

    最近在搞一个电商系统中由于业务需求,需要在插入一条产品信息后返回产品Id,刚开始遇到一些坑,这里做下笔记,以防今后忘记. 类似下面这段代码一样获取插入后的主键 User user = new User ...

  2. Kooboo CMS技术文档之二:Kooboo CMS的安装步骤

    在IIS上安装Kooboo CMS Kooboo CMS安装之后 安装的常见问题 1. 在IIS上安装Kooboo CMS Kooboo CMS部署到正式环境相当简单,安装过程是一个普通MVC站点在I ...

  3. Asp.Net 操作XML文件的增删改查 利用GridView

    不废话,直接上如何利用Asp.NET操作XML文件,并对其属性进行修改,刚开始的时候,是打算使用JS来控制生成XML文件的,但是最后却是无法创建文件,读取文件则没有使用了 index.aspx 文件 ...

  4. spring无法读取properties文件数据

    只讲述异常点,关于怎么配置文件,这里不做说明.   1. controller中无法读取config.properties文件 controller中注入的@Value配置是从servlet-cont ...

  5. 简单酷炫的canvas动画

    作为一个新人怀着激动而紧张的心情写了第一篇帖子还请大家多多支持,小弟在次拜谢. 驯鹿拉圣诞老人动画效果图如下 html如下: <div style="width:400px;heigh ...

  6. Android—Service与Activity的交互

    service-Android的四大组件之一.人称"后台服务"指其本身的运行并不依赖于用户可视的UI界面 实际开发中我们经常需要service和activity之间可以相互传递数据 ...

  7. 张高兴的 UWP 开发笔记:汉堡菜单进阶

    不同于Windows 8应用,Windows 10引入了"汉堡菜单"这一导航模式.说具体点,就拿官方的天气应用来说,左上角三条横杠的图标外加一个SplitView控件组成的这一导航 ...

  8. SqlServer之数据库三大范式

    分析: 数据库设计应遵循三大范式分别为: 第一范式:确保表中每列的原子性(不可拆分): 第二范式:确保表中每列与主键相关,而不能只与主键的某部分相关(主要针对联合主键),主键列与非主键列遵循完全函数依 ...

  9. BZOJ 1391: [Ceoi2008]order [最小割]

    1391: [Ceoi2008]order Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1509  Solved: 460[Submit][Statu ...

  10. 二叉树的创建和遍历(C版和java版)

    以这颗树为例:#表示空节点前序遍历(根->左->右)为:ABD##E##C#F## 中序遍历(左->根->右)为:#D#B#E#A#C#F# 后序遍历(左->右-> ...