iOS 網(wǎng)絡監(jiān)控框架 - Reachability 源碼解讀

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 的接口提供了以下的能力:

  1. 檢測一個域名是否可達,使用 reachabilityWithHostName: 方法龙宏。
  2. 檢測一個 IP 是否可達棵逊,使用 reachabilityWithAddress: 方法。
  3. 檢測設備網(wǎng)絡的可到達性银酗,使用 reachabilityForInternetConnection 方法辆影。
  4. 開始監(jiān)控網(wǎng)絡狀況,使用 startNotifier 方法花吟。
  5. 停止監(jiān)控網(wǎng)絡狀況秸歧,使用 stopNotifier 方法。與 startNotifier 方法配合使用衅澈。
  6. 獲取當前的網(wǎng)絡連接方式键菱,使用 currentReachabilityStatus 方法。
  7. 判斷設備的網(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 源碼解讀邀层。由于個人水平有限,文章若有不對之處懇請指出晴裹,我稍作修改被济,大家共同進步。

參考

  1. Reachability 的代碼和 Demo
    https://developer.apple.com/library/content/samplecode/Reachability/Introduction/Intro.html
  2. SCNetworkReachability 參考
    https://developer.apple.com/documentation/systemconfiguration/scnetworkreachability-g7d
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涧团,一起剝皮案震驚了整個濱河市只磷,隨后出現(xiàn)的幾起案子经磅,更是在濱河造成了極大的恐慌,老刑警劉巖钮追,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件预厌,死亡現(xiàn)場離奇詭異,居然都是意外死亡元媚,警方通過查閱死者的電腦和手機轧叽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刊棕,“玉大人炭晒,你說我怎么就攤上這事∩牵” “怎么了网严?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長嗤无。 經(jīng)常有香客問我震束,道長,這世上最難降的妖魔是什么当犯? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任垢村,我火速辦了婚禮,結果婚禮上嚎卫,老公的妹妹穿的比我還像新娘疏魏。我一直安慰自己字管,他們只是感情好茂蚓,可當我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布交汤。 她就那樣靜靜地躺著,像睡著了一般恰响。 火紅的嫁衣襯著肌膚如雪趣钱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天胚宦,我揣著相機與錄音首有,去河邊找鬼。 笑死枢劝,一個胖子當著我的面吹牛井联,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播您旁,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼烙常,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蚕脏,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤侦副,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后驼鞭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秦驯,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年挣棕,在試婚紗的時候發(fā)現(xiàn)自己被綠了译隘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡洛心,死狀恐怖固耘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情词身,我是刑警寧澤玻驻,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站偿枕,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏户辫。R本人自食惡果不足惜渐夸,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望渔欢。 院中可真熱鬧墓塌,春花似錦、人聲如沸奥额。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽垫挨。三九已至韩肝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間九榔,已是汗流浹背哀峻。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留哲泊,地道東北人剩蟀。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像切威,于是被迫代替她去往敵國和親育特。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容