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