-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathNetworkMonitorService.m
177 lines (151 loc) · 6.27 KB
/
NetworkMonitorService.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#import <sys/socket.h>
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
#import "NetworkMonitorService.h"
@implementation NetworkMonitorService
static void NetworkMonitorServiceCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) {
NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
NSCAssert([(NSObject*)info isKindOfClass:[NetworkMonitorService class]], @"info was wrong class in ReachabilityCallback");
//We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively
// in case someone consumes NetworkMonitorService in a different thread.
NSAutoreleasePool* myPool = [[NSAutoreleasePool alloc] init];
// Notify the client that the network reachability changed.
[(NetworkMonitorService*)info sendStatusChangedNotification];
[myPool release];
}
-(void)sendStatusChangedNotification {
// Post a notification to notify the client that the network reachability changed.
[[NSNotificationCenter defaultCenter] postNotificationName:NetworkStateChangedNotification object:self];
}
-(BOOL)startNotifier{
SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL};
if(SCNetworkReachabilitySetCallback(reachabilityRef, NetworkMonitorServiceCallback, &context) &&
SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
return YES;
}
return NO;
}
-(void)stopNotifier{
if(reachabilityRef != NULL) {
SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
}
-(void)dealloc {
[self stopNotifier];
if(reachabilityRef!= NULL) {
CFRelease(reachabilityRef);
}
[super dealloc];
}
#pragma mark - Network Flag Handling
-(NetworkStatus)localWiFiStatusForFlags:(SCNetworkReachabilityFlags)flags {
BOOL retVal = NetworkStatusNotAvailable;
if((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect)) {
retVal = NetworkStatusWifi;
}
return retVal;
}
-(NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags {
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) {
// if target host is not reachable
return NetworkStatusNotAvailable;
}
if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) {
// WWAN connections are OK if the calling application
// is using the CFNetwork APIs.
return NetworkStatusWWAN;
}
if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) {
// if target host is reachable and no connection is required
// then we'll assume that you're on Wi-Fi
return NetworkStatusWifi;
}
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
return NetworkStatusWifi;
}
}
return NetworkStatusNotAvailable;
}
#pragma mark - Current Status
-(BOOL)connectionRequired {
NSAssert(reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef, use static initializers.");
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
}
return NO;
}
-(NetworkStatus)currentNetworkStatus {
NSAssert(reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef, use static initializers.");
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
return localWiFiRef ? [self localWiFiStatusForFlags:flags] : [self networkStatusForFlags:flags];
}
return NetworkStatusNotAvailable;
}
#pragma mark - Static Initializers
+(NetworkMonitorService*)networkMonitorServiceWithHostName:(NSString*)hostName {
NetworkMonitorService* retVal = NULL;
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
if(reachability!= NULL)
{
retVal= [[[self alloc] init] autorelease];
if(retVal!= NULL)
{
retVal->reachabilityRef = reachability;
retVal->localWiFiRef = NO;
}
}
return retVal;
}
+(NetworkMonitorService*)networkMonitorServiceWithAddress:(const struct sockaddr_in*)hostAddress {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
NetworkMonitorService* retVal = NULL;
if(reachability!= NULL)
{
retVal= [[[self alloc] init] autorelease];
if(retVal!= NULL)
{
retVal->reachabilityRef = reachability;
retVal->localWiFiRef = NO;
}
}
return retVal;
}
+(NetworkMonitorService*)networkMonitorServiceForInternetConnection {
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
return [self networkMonitorServiceWithAddress: &zeroAddress];
}
+(NetworkMonitorService*)networkMonitorServiceForLocalWiFi {
struct sockaddr_in localWifiAddress;
bzero(&localWifiAddress, sizeof(localWifiAddress));
localWifiAddress.sin_len = sizeof(localWifiAddress);
localWifiAddress.sin_family = AF_INET;
// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
NetworkMonitorService* retVal = [self networkMonitorServiceWithAddress: &localWifiAddress];
if(retVal != NULL)
{
retVal->localWiFiRef = YES;
}
return retVal;
}
+(NetworkMonitorService*)sharedService {
static NetworkMonitorService *sharedService = nil;
if (!sharedService) {
sharedService = [[self networkMonitorServiceForInternetConnection] retain];
}
return sharedService;
}
@end