iOS下的实际网络连接状态检测:RealReachability

原文链接:http://blog.csdn.net/openglnewbee/article/details/50705146#comments,我使用了该第三方库,进行了部分改造。感谢作者!!!


序言

网络连接状态检测对于我们的iOS app开发来说是一个非常通用的需求。为了更好的用户体验,我们会在无网络时展现本地或者缓存的内容,并对用户进行合适的提示。对绝大部分iOS开发者来说,从苹果示例代码改变而来的各种Reachablity框架是实现这个需求的普遍选择,比如这个库。但事实上,基于此方案的所有实现,都无法帮助我们检测真正的网络连接状态,它们能检测的只是本地连接状态;这种情况包括但不限于如下场景: 
1.现在很流行的公用wifi,需要网页鉴权,鉴权之前无法上网,但本地连接已经建立; 
2.存在了本地网络连接,但信号很差,实际无法连接到服务器; 
3.iOS连接的路由设备本身没有连接外网。 
cocoachina上已有很多网友对此进行提问和吐槽,比如: 
如何判断设备是否真正连上互联网?而不是只有网络连接 
[Reachability reachabilityWithHostName:]完全没用!

苹果的Reachability示例中有如下说明,告诉我们其能力受限于此: 
“Reachability cannot tell your application if you can connect to a particular host, only that an interface is available that might allow a connection, and whether that interface is the WWAN.” 
苹果的SCNetworkReachability API则告诉了我们更多: 
“Reachability does not guarantee that the data packet will actually be received by the host. ” 
而Reachability相关的所有框架在底层实现都是通过SCNetworkReachability进行检测,所以无法检测实际网络连接情况。 
有鉴于此,笔者希望打造一个通用、简单、可靠的实际网络连接状态检测框架,于是RealReachability诞生了。

RealReachability简单介绍

RealReachability是笔者几个月前发布到github的开源库,目前有1000多个star,200多个fork,几经修改完善后,当前pod版本为1.1.5。 
项目地址如下: 
https://github.com/dustturtle/RealReachability。 
此框架开发的初衷来源于项目实际需求,离线模式对网络连接状态的要求比较苛刻,且实际场景经常会遇到“伪连接”的情况,Reachability面对此场景力不从心。多方研究后引入了ping能力(此方案流量开销最小,也最简单),实现了简单的实际网络连接监测;后面经过提炼和优化,就有了这个框架。可以告诉大家的是,这个框架在appstore上架应用中已经经受了考验,目前也不断完善中,追求稳定的朋友可以使用最新的pod版本(修复了已知的绝大部分问题,参考demo的使用方式即可)。

集成和使用介绍

集成

  • 最简便的集成方法当属pod: pod ‘RealReachability’。
  • 手动集成:将RealReachability文件夹加入到工程即可。
  • 依赖:Xcode5.0+,支持ARC, iOS6+.项目需要引入SystemConfiguration.framework.

使用介绍

其接口的设计和调用方法和Reachability非常相似,大家可以无缝上手,非常方便。 
开启网络监听(建议在didFinishLaunchingWithOptions中进行监听):

<code class="language-objective-c hljs objectivec has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">
- (<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">BOOL</span>)application:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIApplication</span> *)application didFinishLaunchingWithOptions:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSDictionary</span> *)launchOptions {
    [GLobalRealReachability startNotifier];
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">YES</span>;
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>
监听网络变化通知:
<code class="language-objective-c hljs css has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-attr_selector" style="color: rgb(0, 136, 0); box-sizing: border-box;">[[NSNotificationCenter defaultCenter]</span> <span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">addObserver</span><span class="hljs-pseudo" style="color: rgb(0, 0, 0); box-sizing: border-box;">:self</span>
                                             <span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">selector</span>:<span class="hljs-at_rule" style="box-sizing: border-box;">@<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">selector(networkChanged:)</span>
                                                 name:kRealReachabilityChangedNotification
                                               object:nil]</span>;
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>
通知回调代码示例:
<code class="hljs erlang has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-pp" style="box-sizing: border-box;">- <span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(void)</span>networkChanged:<span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-variable" style="box-sizing: border-box;">NSNotification</span> *)</span>notification
{
    RealReachability *reachability = <span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-variable" style="box-sizing: border-box;">RealReachability</span> *)</span>notification</span>.object;
    <span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">ReachabilityStatus</span> status = [reachability current<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">ReachabilityStatus</span>];
    <span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"currentStatus:%@"</span>,@(status));
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>
触发实时网络状态查询代码示例:
<code class="hljs ruby has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">[<span class="hljs-constant" style="box-sizing: border-box;">GLobalRealReachability</span> <span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">reachabilityWithBlock:</span>^(<span class="hljs-constant" style="box-sizing: border-box;">ReachabilityStatus</span> status) {
        switch (status)
        {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> <span class="hljs-constant" style="box-sizing: border-box;">NotReachable</span><span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">:</span>
            {
            <span class="hljs-regexp" style="color: rgb(0, 136, 0); box-sizing: border-box;">//</span>  <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> <span class="hljs-constant" style="box-sizing: border-box;">NotReachable</span> handler
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
            }

            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> <span class="hljs-constant" style="box-sizing: border-box;">ReachableViaWiFi</span><span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">:</span>
            {
            <span class="hljs-regexp" style="color: rgb(0, 136, 0); box-sizing: border-box;">//</span>  <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> <span class="hljs-constant" style="box-sizing: border-box;">ReachableViaWiFi</span> handler
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
            }

            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> <span class="hljs-constant" style="box-sizing: border-box;">ReachableViaWWAN</span><span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">:</span>
            {
            <span class="hljs-regexp" style="color: rgb(0, 136, 0); box-sizing: border-box;">//</span>  <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> <span class="hljs-constant" style="box-sizing: border-box;">ReachableViaWWAN</span> handler
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
            }

            <span class="hljs-function" style="box-sizing: border-box;">default:</span>
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
        }
    }];</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul>
查询当前实际网络连接状态:
<code class="hljs fix has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-attribute" style="box-sizing: border-box;">ReachabilityStatus status </span>=<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;"> [reachability currentReachabilityStatus];</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
设置ping检测用的host服务器地址(可选):

注意:这里你需要确保该服务器支持ping操作。不设置的情况下我们默认使用www.apple.com作为ping服务器。 
可以使用自己的服务器,或者使用稳定、可靠的网络地址(比如百度、qq等)。设置示例如下:

<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">GLobalRealReachability<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.hostForPing</span> = @<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"www.apple.com"</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
获取当前的数据网络连接类型(高级功能):
<code class="hljs bash has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"> WWANAccessType accessType = [GLobalRealReachability currentWWAN<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">type</span>];
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>

该类型可以被用来优化应用程序的体验,比如不同的网络类型下设置不同的网络超时时间等,关于此方面的优化方案,可以参考携程给出的分享:http://www.infoq.com/cn/articles/how-ctrip-improves-app-networking-performance

Demo:

我们在github的repository中已经包含了简单的Demo工程,直接下载运行即可。相关的Api调用也可以参考demo中的实现。 
demo截图: 
这里写图片描述

RealReachability的实现原理

RealReachability架构图:

realReachability架构概要图 
RealReachability主要包含3大模块:connection、ping、FSM; 
其中Ping模块通过对同样是苹果提供的ping样例代码进行了封装,connection模块实现了基于SCNetworkReachability的本地状态检测,FSM模块是有限状态机。通过FSM的状态管理控制connection模块和Ping模块协同工作,并通过可配置的定时策略等业务逻辑优化,最终得到了我们的实现。 
PS:其中connection模块和ping模块也可独立使用,分别提供本地网络检测和ping的能力,感兴趣的读者也可以尝试(调用方式请参考RealReachability开源代码)。

结束语

希望这个框架能够帮助到大家的iOS开发! 遇到任何疑问或者使用上的问题,都可以联系我,期待与您交流iOS开发技术(可以直接在我的博客提问或者email给我).

更新:github上的master版本已经支持IPV6,请开发者尽快升级。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值