Reachability 項目是 Apple 提供的一個官方 Demo, 用于演示如何使用 System Configuration framework 來監(jiān)控 iOS 設備的網(wǎng)絡狀態(tài)蹋偏。值得注意的是 Reachability 僅僅能檢測到數(shù)據(jù)包是否可以離開本設備重归,而不能檢測到數(shù)據(jù)包是否能達到目的地。也就是說不能把它當成 Ping 來使用减噪。Reachability 使用起來也特別的簡單短绸,接下來我們來看看如何使用 Reachability车吹。
Reachability 使用
設置欲檢測的域名,啟動網(wǎng)絡狀態(tài)監(jiān)控醋闭,Reachability 會在設備的網(wǎng)絡狀態(tài)發(fā)生變化的時候會發(fā)出一個名為 kReachabilityChangedNotification 的通知窄驹,我們可以通過接收這個通知,然后根據(jù)設備的網(wǎng)絡狀態(tài)做業(yè)務應對處理证逻。
// 接收 kReachabilityChangedNotification 通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil];
// 檢測某個域名是否可達
self.hostReachability = [Reachability reachabilityWithHostName:remoteHostName];
// 開始監(jiān)控
[self.hostReachability startNotifier];
// 收到 kReachabilityChangedNotification 通知
- (void) reachabilityChanged:(NSNotification *)note
{
Reachability* curReach = [note object];
NSParameterAssert([curReach isKindOfClass:[Reachability class]]);
// 做業(yè)務應對處理
}
Reachability 的組成
Reachability 項目僅僅由一個 Reachability.h 和 Reachability.m 文件組成乐埠。麻雀雖小,五臟俱全囚企。Reachability 也是這樣的丈咐。Reachability 的接口提供了以下的能力:
- 檢測一個域名是否可達,使用 reachabilityWithHostName: 方法龙宏。
- 檢測一個 IP 是否可達棵逊,使用 reachabilityWithAddress: 方法。
- 檢測設備網(wǎng)絡的可到達性银酗,使用 reachabilityForInternetConnection 方法辆影。
- 開始監(jiān)控網(wǎng)絡狀況,使用 startNotifier 方法花吟。
- 停止監(jiān)控網(wǎng)絡狀況秸歧,使用 stopNotifier 方法。與 startNotifier 方法配合使用衅澈。
- 獲取當前的網(wǎng)絡連接方式键菱,使用 currentReachabilityStatus 方法。
- 判斷設備的網(wǎng)絡是否是按需連接方式今布,使用 connectionRequired 方法经备。
@interface Reachability : NSObject
/*!
* Use to check the reachability of a given host name.
*/
+ (instancetype)reachabilityWithHostName:(NSString *)hostName;
/*!
* Use to check the reachability of a given IP address.
*/
+ (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress;
/*!
* Checks whether the default route is available. Should be used by applications that do not connect to a particular host.
*/
+ (instancetype)reachabilityForInternetConnection;
/*!
* Start listening for reachability notifications on the current run loop.
*/
- (BOOL)startNotifier;
- (void)stopNotifier;
- (NetworkStatus)currentReachabilityStatus;
/*!
* WWAN may be available, but not active until a connection has been established. WiFi may require a connection for VPN on Demand.
*/
- (BOOL)connectionRequired;
@end
Reachability 的實現(xiàn)
Reachability 的實現(xiàn)依賴于系統(tǒng)的 SCNetworkReachability 類,SCNetworkReachability 允許應用程序獲取當前系統(tǒng)的網(wǎng)絡配置情況部默,也可以用來判斷一個 target host 的可達性侵蒙。值得注意的是,當應用程序可以將一個 data packet 發(fā)送到 network stack,并且該 data packet 可以離開本地設備傅蹂,這個時候 SCNetworkReachability 就會判斷網(wǎng)絡是可達的纷闺。正是由于這個原因,Reachability 不能保證 data packet 會被送到 target host 份蝴。SCNetworkReachability 的使用分為同步模式和異步模式犁功。
異步模式
我們先從異步模式開始解讀,這畢竟是比較常用的模式婚夫!
第一步
我們從 startNotifier 方法開始浸卦。把 SCNetworkReachabilityRef 放到當前 runloop 的 kCFRunLoopDefaultMode 模式,當網(wǎng)絡連接狀態(tài)發(fā)生變化案糙,SCNetworkReachabilityRef 會執(zhí)行通過 SCNetworkReachabilitySetCallback 方法設定好的 ReachabilityCallback 方法限嫌。
- (BOOL)startNotifier
{
BOOL returnValue = NO;
SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context))
{
//把 SCNetworkReachabilityRef 放到當前 runloop 的 kCFRunLoopDefaultMode 模式
//當 SCNetworkReachabilityRef 判斷到網(wǎng)絡狀態(tài)發(fā)生變化靴庆,會執(zhí)行 ReachabilityCallback 回調(diào)
if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))
{
returnValue = YES;
}
}
return returnValue;
}
第二步
接下來看 ReachabilityCallback 回調(diào)方法,在這里主要是發(fā)送一個 名為 kReachabilityChangedNotification 通知怒医,并將 Reachability 對象作為參數(shù)炉抒,將網(wǎng)絡連接狀態(tài)通知應用程序。
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
{
#pragma unused (target, flags)
NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
NSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback");
// 發(fā)送 kReachabilityChangedNotification 通知稚叹,并將 Reachability 作為參數(shù)
Reachability* noteObject = (__bridge Reachability *)info;
// Post a notification to notify the client that the network reachability changed.
[[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];
}
第三步
startNotifier 和 stopNotifier 方法是配合使用的端礼。stopNotifier 的主要任務是對 startNotifier 做的操作進行逆向處理。把 SCNetworkReachabilityRef 從當前 runloop 的 kCFRunLoopDefaultMode 模式移除入录。
- (void)stopNotifier
{
if (_reachabilityRef != NULL)
{
SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
}
第四步
上面主要是主流程解讀,解讀 Reachability 開始網(wǎng)絡監(jiān)控佳镜,網(wǎng)絡狀態(tài)發(fā)生變化處理僚稿,到停止網(wǎng)絡監(jiān)控的流程。這過程 Reachability 都
依賴于 SCNetworkReachabilityRef 的對象蟀伸,接下來我們來說說 Reachability 怎么創(chuàng)建 SCNetworkReachabilityRef 對象蚀同。SCNetworkReachabilityRef 對象的創(chuàng)建依賴于 IP 或者域名。依賴于域名使用 SCNetworkReachabilityCreateWithName 方法啊掏,依賴于 IP 使用 SCNetworkReachabilityCreateWithAddress 方法蠢络。
// 通過域名創(chuàng)建 SCNetworkReachabilityRef 對象
+ (instancetype)reachabilityWithHostName:(NSString *)hostName
{
Reachability* returnValue = NULL;
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
if (reachability != NULL)
{
returnValue= [[self alloc] init];
if (returnValue != NULL)
{
returnValue->_reachabilityRef = reachability;
}
else {
CFRelease(reachability);
}
}
return returnValue;
}
// 通過 IP 創(chuàng)建 SCNetworkReachabilityRef 對象
+ (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress
{
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, hostAddress);
Reachability* returnValue = NULL;
if (reachability != NULL)
{
returnValue = [[self alloc] init];
if (returnValue != NULL)
{
returnValue->_reachabilityRef = reachability;
}
else {
CFRelease(reachability);
}
}
return returnValue;
}
以上是異步模式的解讀,Reachability 的異步模式顧名思義就是網(wǎng)絡連接發(fā)生了變化迟蜜,Reachability 發(fā)通知告知應用程序刹孔。而同步模式呢? 那就是應用程序主動找 Reachability 獲取當前的網(wǎng)絡連接狀態(tài)娜睛。
同步模式
應用程序主動找 Reachability 獲取當前的網(wǎng)絡連接狀態(tài)髓霞,使用 currentReachabilityStatus 方法。Reachability 能夠?qū)崿F(xiàn)同步模式依賴于 SCNetworkReachabilityRef 的 SCNetworkReachabilityGetFlags 方法畦戒。SCNetworkReachabilityGetFlags 方法使用同步模式獲取設備的網(wǎng)絡連接狀態(tài)方库。
- (NetworkStatus)currentReachabilityStatus
{
NSAssert(_reachabilityRef != NULL, @"currentNetworkStatus called with NULL SCNetworkReachabilityRef");
NetworkStatus returnValue = NotReachable;
SCNetworkReachabilityFlags flags;
// 同步模式獲取網(wǎng)絡連接狀態(tài)
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
{
returnValue = [self networkStatusForFlags:flags];
}
return returnValue;
}
總結
Reachability 可以用來檢測網(wǎng)絡狀態(tài)變化和網(wǎng)絡的可達性的一個框架。但是 Reachability 僅僅能檢測到數(shù)據(jù)包是否可以離開本設備障斋,而不能檢測到數(shù)據(jù)包是否能達到目的地纵潦,也就是說不能把它當成 Ping 來使用。若是想了解 ping 功能垃环,可以參考我的另一篇文章 iOS ping - SimplePing 源碼解讀邀层。由于個人水平有限,文章若有不對之處懇請指出晴裹,我稍作修改被济,大家共同進步。
參考
- Reachability 的代碼和 Demo
https://developer.apple.com/library/content/samplecode/Reachability/Introduction/Intro.html - SCNetworkReachability 參考
https://developer.apple.com/documentation/systemconfiguration/scnetworkreachability-g7d