学习 OC网络状态获取

MyProjectNetEnv.h

//
//  MyProjectNetEnv.h
//  LearnReachability
//
//  Created by maochengfang on 2021/3/21.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN


#define QCloudNetworkShareEnv [QCloudNetEnv shareEnv]

typedef NS_ENUM(NSInteger, MyProjectNetworkStatus) {
    MyProjectNotReachable = 0,
    MyProjectReachableViaWiFi = 2,
    MyProjectReachableViaWWAN = 1,
};

#define kNetworkSituationChangeKey @"kNetworkSituationChangeKey"
/**
 表明当前网络的情况,是弱网络或者网络情况良好

 - QCloudNetworkSituationWeakNetwork: 当前是弱网络,延迟和丢包率较高
 - QCloudNetworkSituationGreatNetork: 当前网络较好,可以适当增加并发数
 */
typedef NS_ENUM(NSInteger, MyProjectNetworkSituation) { MyProjectNetworkSituationWeakNetwork = 0, MyProjectNetworkSituationGreatNetork = 1 };

extern NSString *const kQCloudNetEnvChangedNotification;
NSString * QCloudNetworkSituationToString(MyProjectNetworkStatus situation);

@interface MyProjectNetEnv : NSObject

+ (instancetype)shareEnv;
@property (nonatomic, assign, readonly) MyProjectNetworkStatus currentNetStatus;
- (BOOL)isReachableViaWifi;
- (BOOL)isReachableVia2g3g4g;
- (BOOL)isReachable;


@end

NS_ASSUME_NONNULL_END

MyProjectNetEnv.m

//
//  MyProjectNetEnv.m
//  LearnReachability
//
//  Created by maochengfang on 2021/3/21.
//

#import "MyProjectNetEnv.h"
#import "MyProjectReachability.h"

NSString *const MyProjectNetEnvChangedNotification = @"kMyProjectNetEnvChangedNotification";

NSString * MyProjectNetworkSituationToString(MyProjectNetworkStatus situation){
    switch (situation) {
        case MyProjectReachableViaWiFi:
            return @"WIFI";
            break;
        case MyProjectReachableViaWWAN:
            return @"WWAN";
            break;
        default:
            return @"NONE";
            break;
    }
    return @"NONE";
}

@implementation MyProjectNetEnv{
    MyProjectReachability *_reachAbility;
    BOOL _isInit;
}

@synthesize currentNetStatus = _currentNetStatus;
- (void)dealloc {
    [_reachAbility stopNotifier];
}

+ (instancetype)shareEnv {
    static MyProjectNetEnv *env = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        env = [MyProjectNetEnv new];
    });
    return env;
}

- (instancetype)init {
    self = [super init];
    if (!self) {
        return self;
    }
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networChanged:) name:MyProjectReachabilityChangedNotification object:nil];
    _reachAbility = [MyProjectReachability reachabilityWithHostname:@"www.tencent.com"];
    [_reachAbility startNotifier];
    _isInit = NO;
    return self;
}

- (void)networChanged:(NSNotification *)nc {
    id object = nc.object;
    if (object != _reachAbility) {
        return;
    }
    switch (_reachAbility.currentReachabilityStatus) {
        case NotReachable:
            _currentNetStatus = MyProjectNotReachable;
            break;
        case ReachableViaWiFi:
            MyProject;
            break;
        case ReachableViaWWAN:
            _currentNetStatus = MyProjectReachableViaWWAN;
            break;
        default:
            MyProject;
            break;
    }
    if (!_isInit) {
        _isInit = YES;
    }
    [[NSNotificationCenter defaultCenter] postNotificationName:MyProjectNetEnvChangedNotification object:self];
}

- (MyProjectNetworkStatus)currentNetStatus {
    if (!_isInit) {
        if ([self isReachableViaWifi]) {
            MyProject;
        } else if ([self isReachableVia2g3g4g]) {
            _currentNetStatus = MyProjectReachableViaWWAN;
        } else if (![_reachAbility isReachable]) {
            _currentNetStatus = MyProjectNotReachable;
        } else {
            MyProject;
        }
        _isInit = YES;
    }
    return _currentNetStatus;
}

- (BOOL)isReachableViaWifi {
    return [_reachAbility isReachableViaWiFi];
}

- (BOOL)isReachableVia2g3g4g {
    return [_reachAbility isReachableViaWWAN];
}

- (BOOL)isReachable {
    return [_reachAbility isReachable];
}


@end

MyProjectReachability.h

//
//  MyProjectReachability.h
//  LearnReachability
//
//  Created by maochengfang on 2021/3/21.
//

#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>

//! Project version number for MacOSReachability.
FOUNDATION_EXPORT double MyProjectReachabilityVersionNumber;

//! Project version string for MacOSReachability.
FOUNDATION_EXPORT const unsigned char MyProjectReachabilityVersionString[];

#ifndef NS_ENUM
#define NE_ENUM(_type, name) \
    enum _name : _type _name;\
    enum _name : _type
#endif

NS_ASSUME_NONNULL_BEGIN

extern NSString* const   MyProjectReachabilityChangedNotification;

typedef NS_ENUM (NSInteger, NetworkStatus) {
    NotReachable = 0,
    ReachableViaWiFi = 2,
    ReachableViaWWAN = 1,
};

@class MyProjectReachability;

typedef void (^MyProjectNetworkReachable)(MyProjectReachability *reachability);
typedef void (^MyProjectNetworkUnreachable)(MyProjectReachability *reachability);
typedef void (^MyProjectNetworkReachability)(MyProjectReachability *reachability, SCNetworkConnectionFlags flags);

@interface MyProjectReachability : NSObject

@property (nonatomic, copy) MyProjectNetworkReachable reachableBlock;
@property (nonatomic, copy) MyProjectNetworkUnreachable unreachableBlock;
@property (nonatomic, copy) MyProjectNetworkReachability rechabilityBlock;

@property (nonatomic, assign) BOOL reachableOnWWAN;

+ (instancetype)reachabilityWithHostname:(NSString *)hostname;
+ (instancetype)reachabilityWithHostName:(NSString *)hostname;

+ (instancetype)reachabilityForInternetConnection;

+ (instancetype)reachabilityWithAddress:(void *)hostAddress;

+ (instancetype)reachabilityForLocalWiFi;

- (instancetype)initWithReachabilityRef:(SCNetworkReachabilityRef)ref;

- (BOOL)startNotifier;
- (void)stopNotifier;

- (BOOL)isReachable;
- (BOOL)isReachableViaWWAN;
- (BOOL)isReachableViaWiFi;
// WWAN may be available, but not active until a connection has been established.
// WiFi may require a connection for VPN on Demand.

- (BOOL)isConnectionRequired; //Identical DDG variant.
-(BOOL)connectionRequired; // Apple's routine.
// Dynamic, on demand connection?
- (BOOL)isConnectionOnDemand;
// Is user intervention required?
- (BOOL)isInterventionRequired;

- (NetworkStatus)currentReachabilityStatus;
- (SCNetworkReachabilityFlags)reachabilityFlags;
- (NSString *)currentReachabilityString;
- (NSString *)currentReachabilityFlags;

@end

NS_ASSUME_NONNULL_END

MyProjectReachability.m

//
//  MyProjectReachability.m
//  LearnReachability
//
//  Created by maochengfang on 2021/3/21.
//

#import "MyProjectReachability.h"

#import <sys/socket.h>
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>

NSString *const MyProjectReachabilityChangedNotification = @"kReachabilityChangedNotification";

@interface MyProjectReachability ()

@property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef;
@property (nonatomic, strong) dispatch_queue_t reachabilitySerialQueue;
@property (nonatomic, strong) id reachabilityObject;

- (void)reachabilityChanged:(SCNetworkReachabilityFlags)flags;
- (BOOL)isReachableWithFlag:(SCNetworkReachabilityFlags)flags;

@end

static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags) {
    return [NSString stringWithFormat:@"%c%c %c%c%c%c%c%c%c",
#if TARGET_OS_IPHONE
                                      (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
#else
                                      'X',
#endif
                                      (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
                                      (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
                                      (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
                                      (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
                                      (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
                                      (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
                                      (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
                                      (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-'];
}

// Start listening for reachability notifications on the current run loop
static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) {
#pragma unused(target)

    MyProjectReachability *reachability = ((__bridge MyProjectReachability *)info);

    // We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool,
    // but what the heck eh?
    @autoreleasepool {
        [reachability reachabilityChanged:flags];
    }
}

@implementation MyProjectReachability

+ (instancetype)reachabilityWithHostName:(NSString *)hostname{
    
    return [MyProjectReachability reachabilityWithHostname:hostname];
}

+ (instancetype)reachabilityWithHostname:(NSString *)hostname{
    
    SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
    
    if (ref) {
        id reachability = [[self alloc] initWithReachabilityRef:ref];
        return  reachability;
    }
    return nil;
}

+ (instancetype)reachabilityWithAddress:(void *)hostAddress {
    SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)hostAddress);
    if (ref) {
        id reachability = [[self alloc] initWithReachabilityRef:ref];

        return reachability;
    }

    return nil;
}

+ (instancetype)reachabilityForInternetConnection{
    struct sockaddr_in zeroAddress;
    
    bzero(&zeroAddress, sizeof(zeroAddress));
    
    zeroAddress.sin_len = sizeof(zeroAddress);
    zeroAddress.sin_family = AF_INET;
    
    return [self reachabilityWithAddress:&zeroAddress];;
}

+ (instancetype)reachabilityForLocalWiFi{
    
    struct sockaddr_in localWifiAddress;
    
    bzero(&localWifiAddress, sizeof(localWifiAddress));
    localWifiAddress.sin_len = sizeof(localWifiAddress);
    localWifiAddress.sin_family = AF_INET;
    localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
    
    return [self reachabilityWithAddress:&localWifiAddress];
}

- (instancetype)initWithReachabilityRef:(SCNetworkReachabilityRef)ref{
    
    self  = [super init];
    
    if (self != nil) {
        
        self.reachableOnWWAN = YES;
        self.reachabilityRef = ref;
        
        // We need to create a serial queue.
        // We allocate this once for the lifetime of the notifier.
        
        self.reachabilitySerialQueue = dispatch_queue_create("com.oliver.reachability", NULL);
        
    }
    
    return self;
}

- (void)dealloc{
    
    [self stopNotifier];
    if (self.reachabilityRef) {
        CFRelease(self.reachabilityRef);
        self.reachabilityRef  = nil;
    }
    
    self.reachableBlock = nil;
    self.unreachableBlock = nil;
    self.rechabilityBlock = nil;
    self.reachabilitySerialQueue = nil;
}

- (BOOL)startNotifier{
    //允许开启通知 可以被多次调用
    if (self.reachabilityObject && (self.reachabilityObject = self)) {
        return YES;
    }
    
    SCNetworkReachabilityContext context = {0,NULL,NULL,NULL};
    
    context.info = (__bridge void *)self;
    
    if (SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context)) {
        //将它设置成可达到队列 将被在队列中持有
        if (SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue)) {
            self.reachabilityObject = self;
            //一直持有它
            return YES;
        }else{
            #ifdef DEBUG
                        NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError()));
            #endif
            SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
        }
    }else{
            #ifdef DEBUG
                    NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError()));
            #endif
    }
    
    self.reachabilityObject = nil;
    
    return NO;
}

- (void)stopNotifier{
    //首先终止 任何回调
    SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
    
    //在队列中不再注册
    SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL);
    self.reachabilityObject = nil;
}


#define testcase (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection)

- (BOOL)isReachableWithFlag:(SCNetworkReachabilityFlags)flags{
    
    BOOL connectionUP = YES;
    
    if (!(flags & kSCNetworkReachabilityFlagsReachable)) {
        connectionUP = NO;
    }
    
    if ((flags & testcase) == testcase) {
        connectionUP = NO;
    }
    
#if TARGET_OS_IPHONE
    if (!(flags & kSCNetworkReachabilityFlagsIsWWAN)) {
        if (!self.reachableOnWWAN) {
            connectionUP = NO;
        }
    }

#endif
    return connectionUP;
}

- (BOOL)isReachable{
    
    SCNetworkReachabilityFlags flags;
    if (!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) {
        
        return NO;
    }
    
    return [self isReachableWithFlag:flags];
}

- (BOOL)isReachableViaWWAN {
#if TARGET_OS_IPHONE

    SCNetworkReachabilityFlags flags = 0;

    if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) {
        // Check we're REACHABLE
        if (flags & kSCNetworkReachabilityFlagsReachable) {
            // Now, check we're on WWAN
            if (flags & kSCNetworkReachabilityFlagsIsWWAN) {
                return YES;
            }
        }
    }
#endif

    return NO;
}

- (BOOL)isReachableViaWiFi {
    SCNetworkReachabilityFlags flags = 0;

    if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) {
        // Check we're reachable
        if ((flags & kSCNetworkReachabilityFlagsReachable)) {
#if TARGET_OS_IPHONE
            // Check we're NOT on WWAN
            if ((flags & kSCNetworkReachabilityFlagsIsWWAN)) {
                return NO;
            }
#endif
            return YES;
        }
    }

    return NO;
}

- (BOOL)isConnectionRequired{
    
    return [self connectionRequired];
}

- (BOOL)connectionRequired{
    
    SCNetworkReachabilityFlags flags;

    if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) {
        
        return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
    }
    return NO;
}

- (BOOL)isConnectionOnDemand{
    
    SCNetworkReachabilityFlags flags;
    
    if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) {
        
        return ((flags & kSCNetworkReachabilityFlagsConnectionRequired)
                && (flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand)));;
    }
    return NO;
}

- (BOOL)isInterventionRequired{
    
    SCNetworkReachabilityFlags flags;

    if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) {
        return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) && (flags & kSCNetworkReachabilityFlagsInterventionRequired));
    }

    return NO;
}

- (NetworkStatus)currentReachabilityStatus{
    
    if ([self isReachable]) {
        if ([self isReachableViaWiFi]) {
            return ReachableViaWiFi;
            }
    #if TARGET_OS_IPHONE
            return ReachableViaWWAN;
    #endif
    }
    return NotReachable;
}

- (SCNetworkReachabilityFlags)reachabilityFlags{
    
    SCNetworkReachabilityFlags flags = 0;
    if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) {
        return flags;
    }
        return 0;
}

- (NSString *)currentReachabilityString{
    
    NetworkStatus temp = [self currentReachabilityStatus];
    
    if (temp == ReachableViaWWAN) {
        return NSLocalizedString(@"Cellular",@"");
    }
    if (temp == ReachableViaWiFi) {
        return NSLocalizedString(@"Wifi",@"");
    }
    return NSLocalizedString(@"No Connection",@"");;
}

- (NSString *)currentReachabilityFlags{
    
    return reachabilityFlags([self reachabilityFlags]);
}

- (void)reachabilityChanged:(SCNetworkReachabilityFlags)flags{
    
    if ([self isReachableWithFlag:flags]) {
        if (self.reachableBlock) {
            self.reachableBlock(self);
        }
    }else{
        if (self.unreachableBlock) {
            self.unreachableBlock(self);
        }
    }
    
    if (self.rechabilityBlock) {
        self.rechabilityBlock(self, flags);
    }
    
    dispatch_async(dispatch_get_main_queue(), ^{
       
        [[NSNotificationCenter defaultCenter] postNotificationName:MyProjectReachabilityChangedNotification object:self];
    });
}

#pragma mark - Debug Description

- (NSString *)description {
    NSString *description =
        [NSString stringWithFormat:@"<%@: %#x (%@)>", NSStringFromClass([self class]), (unsigned int)self, [self currentReachabilityFlags]];
    return description;
}

@end

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值