iOS学习之Reachability(一)

作者: c1442725faa6 | 来源:发表于2016-05-10 16:49 被阅读6069次

    Reachability

    Reachability 展示了如何来监听iOS设备的网络状态。可以监听什么时候IP地址可以被连接,什么时候WWAN(EDGE或者3G网络)失去连接。

    注: Reachability 不会告诉你的应用你是否可以连接到一个特定的主机,只有一个接口是可用的,才会允许进行连接,并告诉你那个连接是否是WANN。 想要了解什么时候以及怎么使用Reachability Networking Overview.

    IPv6 支持

    Reachability 完全支持 IPv6 协议. API通过以下方式来操作IPv6:

    • reachabilityWithHostName:andSCNetworkReachabilityCreateWithName:
      在内部,这个API用来解决一组IP地址的主机名 (也可以是IPv4和IPv6的混合地址) 并且在不同的可用地址中设置分别监听。

    • reachabilityWithAddress:andSCNetworkReachabilityCreateWithAddress:
      为了监听IPv6地址,简单的传入一个IPv6sockaddr_in6 struct 来代替IPv4 sockaddr_in struct.

    • reachabilityForInternetConnection:
      在IPv4和IPv6协议下,此API显示一个地址为(0.0.0.0),Reachability会把它当做一个用来监听通常状态下设备连接状态的特殊授权。

    苹果官方demo

    Reachability.h

    #import <Foundation/Foundation.h>
    #import <SystemConfiguration/SystemConfiguration.h>
    #import <netinet/in.h>
    
    
    typedef enum : NSInteger {
        NotReachable = 0,
        ReachableViaWiFi,
        ReachableViaWWAN
    } NetworkStatus;
    
    #pragma mark IPv6 协议支持
    // Reachability完全支持IPv6协议.
    
    
    extern NSString *kReachabilityChangedNotification;
    
    
    @interface Reachability : NSObject
    
    /*!
     * 用来检测hostName的连接状态.
     */
    + (instancetype)reachabilityWithHostName:(NSString *)hostName;
    
    /*!
     * 用来检测IP地址的连接状态.
     */
    + (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress;
    
    /*!
     * 用来检测默认的连接是否可用, 被用在没有特定连接的主机.
     */
    + (instancetype)reachabilityForInternetConnection;
    
    /*!
     *开始在当前时间循环中监听Reachability通知.
     */
    - (BOOL)startNotifier;
    - (void)stopNotifier;
    
    - (NetworkStatus)currentReachabilityStatus;
    
    /*!
     * 除非连接已经建立,WWAN可用,但是却没有激活. WiFi 连结也许需要一个VPN来进行连接.
     */
    - (BOOL)connectionRequired;
    
    @end
    

    Reachability.m

    #import <arpa/inet.h>
    #import <ifaddrs.h>
    #import <netdb.h>
    #import <sys/socket.h>
    #import <netinet/in.h>
    
    #import <CoreFoundation/CoreFoundation.h>
    
    #import "Reachability.h"
    
    #pragma mark IPv6 协议支持
    // Reachability完全支持IPv6协议.
    
    
    NSString *kReachabilityChangedNotification = @"kNetworkReachabilityChangedNotification";
    
    
    #pragma mark - 支持Reachability的方法
    
    #define kShouldPrintReachabilityFlags 1
    
    static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment)
    {
    #if kShouldPrintReachabilityFlags
    
        NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
              (flags & kSCNetworkReachabilityFlagsIsWWAN)               ? 'W' : '-',
              (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',
    
              (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
              (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
              (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',
              (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
              (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',
              (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
              (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-',
              comment
              );
    #endif
    }
    
    
    static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
    {
    #pragma 没有用的(target, flags)
        NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
        NSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback");
    
        Reachability* noteObject = (__bridge Reachability *)info;
        // Post a notification to notify the client that the network reachability changed.
        [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];
    }
    
    
    #pragma mark - Reachability的实现
    
    @implementation Reachability
    {
        SCNetworkReachabilityRef _reachabilityRef;
    }
    
    + (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;
    }
    
    
    + (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;
    }
    
    
    + (instancetype)reachabilityForInternetConnection
    {
        struct sockaddr_in zeroAddress;
        bzero(&zeroAddress, sizeof(zeroAddress));
        zeroAddress.sin_len = sizeof(zeroAddress);
        zeroAddress.sin_family = AF_INET;
        
        return [self reachabilityWithAddress: (const struct sockaddr *) &zeroAddress];
    }
    
    
    
    
    #pragma mark - 开始和结束监听通知
    
    - (BOOL)startNotifier
    {
        BOOL returnValue = NO;
        SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
    
        if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context))
        {
            if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))
            {
                returnValue = YES;
            }
        }
        
        return returnValue;
    }
    
    
    - (void)stopNotifier
    {
        if (_reachabilityRef != NULL)
        {
            SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
        }
    }
    
    
    - (void)dealloc
    {
        [self stopNotifier];
        if (_reachabilityRef != NULL)
        {
            CFRelease(_reachabilityRef);
        }
    }
    
    
    #pragma mark - Network Flag Handling
    
    - (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags
    {
        PrintReachabilityFlags(flags, "networkStatusForFlags");
        if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
        {
            // The target host is not reachable.
            return NotReachable;
        }
    
        NetworkStatus returnValue = NotReachable;
    
        if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
        {
            /*
             If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi...
             */
            returnValue = ReachableViaWiFi;
        }
    
        if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
            (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
        {
            /*
             ... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs...
             */
    
            if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
            {
                /*
                 ... and no [user] intervention is needed...
                 */
                returnValue = ReachableViaWiFi;
            }
        }
    
        if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
        {
            /*
             ... but WWAN connections are OK if the calling application is using the CFNetwork APIs.
             */
            returnValue = ReachableViaWWAN;
        }
        
        return returnValue;
    }
    
    
    - (BOOL)connectionRequired
    {
        NSAssert(_reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
        SCNetworkReachabilityFlags flags;
    
        if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
        {
            return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
        }
    
        return NO;
    }
    
    
    - (NetworkStatus)currentReachabilityStatus
    {
        NSAssert(_reachabilityRef != NULL, @"currentNetworkStatus called with NULL SCNetworkReachabilityRef");
        NetworkStatus returnValue = NotReachable;
        SCNetworkReachabilityFlags flags;
        
        if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
        {
            returnValue = [self networkStatusForFlags:flags];
        }
        
        return returnValue;
    }
    
    
    @end
    

    APLViewController.m

    #import "APLViewController.h"
    #import "Reachability.h"
    
    @interface APLViewController ()
    
    @property (nonatomic, weak) IBOutlet UILabel* summaryLabel;
    
    @property (nonatomic, weak) IBOutlet UITextField *remoteHostLabel;
    @property (nonatomic, weak) IBOutlet UIImageView *remoteHostImageView;
    @property (nonatomic, weak) IBOutlet UITextField *remoteHostStatusField;
    
    @property (nonatomic, weak) IBOutlet UIImageView *internetConnectionImageView;
    @property (nonatomic, weak) IBOutlet UITextField *internetConnectionStatusField;
    
    @property (nonatomic) Reachability *hostReachability;
    @property (nonatomic) Reachability *internetReachability;
    
    @end
    
    
    @implementation APLViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        self.summaryLabel.hidden = YES;
    
        /*
        监听 kNetworkReachabilityChangedNotification通知. 当监听到通知中心发出通知kNetworkReachabilityChangedNotification的时候, reachabilityChanged 将会被调用.
         */
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil];
    
        // 在这里改变主机名来改变你想要监听的服务.
        NSString *remoteHostName = @"www.apple.com";
        NSString *remoteHostLabelFormatString = NSLocalizedString(@"Remote Host: %@", @"Remote host label format string");
        self.remoteHostLabel.text = [NSString stringWithFormat:remoteHostLabelFormatString, remoteHostName];
        
        self.hostReachability = [Reachability reachabilityWithHostName:remoteHostName];
        [self.hostReachability startNotifier];
        [self updateInterfaceWithReachability:self.hostReachability];
    
        self.internetReachability = [Reachability reachabilityForInternetConnection];
        [self.internetReachability startNotifier];
        [self updateInterfaceWithReachability:self.internetReachability];
    
    }
    
    /*!
     * 当状态改变的时候,由Reachability调用
     */
    - (void) reachabilityChanged:(NSNotification *)note
    {
        Reachability* curReach = [note object];
        NSParameterAssert([curReach isKindOfClass:[Reachability class]]);
        [self updateInterfaceWithReachability:curReach];
    }
    
    
    - (void)updateInterfaceWithReachability:(Reachability *)reachability
    {
        if (reachability == self.hostReachability)
        {
            [self configureTextField:self.remoteHostStatusField imageView:self.remoteHostImageView reachability:reachability];
            NetworkStatus netStatus = [reachability currentReachabilityStatus];
            BOOL connectionRequired = [reachability connectionRequired];
    
            self.summaryLabel.hidden = (netStatus != ReachableViaWWAN);
            NSString* baseLabelText = @"";
            
            if (connectionRequired)
            {
                baseLabelText = NSLocalizedString(@"Cellular data network is available.\nInternet traffic will be routed through it after a connection is established.", @"Reachability text if a connection is required");
            }
            else
            {
                baseLabelText = NSLocalizedString(@"Cellular data network is active.\nInternet traffic will be routed through it.", @"Reachability text if a connection is not required");
            }
            self.summaryLabel.text = baseLabelText;
        }
        
        if (reachability == self.internetReachability)
        {
            [self configureTextField:self.internetConnectionStatusField imageView:self.internetConnectionImageView reachability:reachability];
        }
    
    }
    
    
    - (void)configureTextField:(UITextField *)textField imageView:(UIImageView *)imageView reachability:(Reachability *)reachability
    {
        NetworkStatus netStatus = [reachability currentReachabilityStatus];
        BOOL connectionRequired = [reachability connectionRequired];
        NSString* statusString = @"";
        
        switch (netStatus)
        {
            case NotReachable:        {
                statusString = NSLocalizedString(@"Access Not Available", @"Text field text for access is not available");
                imageView.image = [UIImage imageNamed:@"stop-32.png"] ;
                /*
                 Minor interface detail- connectionRequired may return YES even when the host is unreachable. We cover that up here...
                 */
                connectionRequired = NO;
                break;
            }
    
            case ReachableViaWWAN:        {
                statusString = NSLocalizedString(@"Reachable WWAN", @"");
                imageView.image = [UIImage imageNamed:@"WWAN5.png"];
                break;
            }
            case ReachableViaWiFi:        {
                statusString= NSLocalizedString(@"Reachable WiFi", @"");
                imageView.image = [UIImage imageNamed:@"Airport.png"];
                break;
            }
        }
        
        if (connectionRequired)
        {
            NSString *connectionRequiredFormatString = NSLocalizedString(@"%@, Connection Required", @"Concatenation of status string with connection requirement");
            statusString= [NSString stringWithFormat:connectionRequiredFormatString, statusString];
        }
        textField.text= statusString;
    }
    
    
    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil];
    }
    
    @end

    相关文章

      网友评论

      • 程旭媛:请问如何像酷我音乐盒那样实现“仅使用wifi联网”的功能呢?
      • feng_dev:不过感觉写的有点复杂啊。那些常用的 标注的不清楚,我是小菜鸟
      • feng_dev:reach 到底是官方框架 还是 三方库啊
        c1442725faa6:@Coder_枫 :smile:
        feng_dev:@JohnsonSmile 哦哦,还有这么一说的,学习了O(∩_∩)O哈哈~
        c1442725faa6:@Coder_枫 官方框架,只是没有集成到Xcode里,所以说是苹果官方的第三方库。

      本文标题:iOS学习之Reachability(一)

      本文链接:https://www.haomeiwen.com/subject/gvdyrttx.html