Skip to content

Commit 57f631b

Browse files
Fixed issue with NetAssociations not being accounted for offsetAverage calculation. Added new NetworkClock factory method.
1 parent 11596e1 commit 57f631b

File tree

7 files changed

+177
-25
lines changed

7 files changed

+177
-25
lines changed

Code/NetAssociation.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
/**
5959
Returns Net Association statistics.
6060
61-
Returned stats are: leap_indicator, version_number, protocol_mode, stratum, poll interval, precision_exp, root_delay, dispersion, reference_ID
61+
Returned stats are: server, leap_indicator, version_number, protocol_mode, stratum, poll interval, precision_exp, root_delay, dispersion, reference_ID
6262
6363
@return NSDictionary with receiver stats.
6464
*/

Code/NetAssociation.m

+3-3
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ - (NSData *) createPacket {
241241
struct timeval now;
242242
gettimeofday(&now, NULL);
243243

244-
ntpClientSendTime.fullSeconds = now.tv_sec + JAN_1970;
244+
ntpClientSendTime.fullSeconds = (uint32_t) now.tv_sec + JAN_1970;
245245
ntpClientSendTime.partSeconds = uSec2Frac(now.tv_usec);
246246

247247
wireData[10] = htonl(now.tv_sec + JAN_1970); // Transmit Timestamp
@@ -343,7 +343,7 @@ - (BOOL) onUdpSocket:(AsyncUdpSocket *)sender didReceiveData:(NSData *)data
343343
struct timeval arrival_time;
344344
gettimeofday(&arrival_time, NULL);
345345

346-
ntpClientRecvTime.fullSeconds = arrival_time.tv_sec + JAN_1970; // Transmit Timestamp coarse
346+
ntpClientRecvTime.fullSeconds = (uint32_t) arrival_time.tv_sec + JAN_1970; // Transmit Timestamp coarse
347347
ntpClientRecvTime.partSeconds = uSec2Frac(arrival_time.tv_usec); // Transmit Timestamp fine
348348

349349
uint32_t hostData[12];
@@ -480,7 +480,7 @@ - (NSString *) description {
480480

481481
- (NSDictionary*)stats {
482482
NSMutableDictionary *stats = [NSMutableDictionary dictionary];
483-
483+
stats[@"server"] = server;
484484
stats[@"leap_indicator"] = @(li);
485485
stats[@"version_number:"] = @(vn);
486486
stats[@"protocol_mode"] = @(mode);

Code/NetworkClock.h

+47-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,30 @@
1111
/**
1212
The NetworkClock is a singleton class which will provide the best estimate of the difference in time
1313
between the device's system clock and the time returned by a collection of time servers.
14+
15+
NetworkClock can be initialized with NTP hosts file:
16+
17+
[[NetworkClock clockWithNTPHostsFile:[[NSBundle mainBundle] pathForResource:@"ntp.hosts" ofType:@""]];
18+
NSDate *networkTime = [[NetworkClock sharedInstance] networkTime];
19+
20+
// ------ ntp.hosts file ------
21+
time.apple.com
22+
192.168.123.12 alwaystrust
23+
#ignored line
24+
// --- (END) ntp.hosts file ---
25+
26+
Or it can be initialized with NTP hosts array:
27+
28+
[[NetworkClock clockWithNTPHosts:@[
29+
@"time.apple.com",
30+
@"192.168.123.12 alwaystrust" // 'alwaystrust' flag ignores dispersion and stratum check for this NTP server
31+
]];
32+
NSDate *networkTime = [[NetworkClock sharedInstance] networkTime];
33+
34+
Invoking [NetworkClock sharedInstance] before either [NetworkClock clockWithNTPHostsFile:] or [NetworkClock clockWithNTPHosts:] is equavilent to:
35+
36+
[[NetworkClock clockWithNTPHostsFile:[[NSBundle mainBundle] pathForResource:@"ntp.hosts" ofType:@""]];
37+
1438
*/
1539
@interface NetworkClock : NSObject
1640

@@ -30,7 +54,7 @@
3054
3155
@param instance The new shared instance.
3256
*/
33-
+ (void)setSharedInstance:(NetworkClock*)instance;
57+
+ (void)setSharedInstance:(NetworkClock *)instance;
3458

3559
///-------------------------------------
3660
/// @name Initializing an Network Clock
@@ -42,7 +66,17 @@
4266
@param filePath The full path to NTP hosts file with wich to initialize `NetAssociation` objects.
4367
@return A new `NetworkClock` object initialized with the given hosts file.
4468
*/
45-
+ (instancetype)clockNTPHostsFile:(NSString*)filePath;
69+
+ (instancetype)clockWithNTPHostsFile:(NSString *)filePath;
70+
71+
72+
/**
73+
Creates and returns a new `NetworkClock` object initialized with a list of `NetAssociation` objects that was in turn initialized with the given NTP hosts array.
74+
75+
@param ntpHosts The NSArray of NSString NTP hosts with wich to initialize `NetAssociation` objects.
76+
@return A new `NetworkClock` object initialized with the given hosts array.
77+
*/
78+
79+
+ (instancetype)clockWithNTPHosts:(NSArray *)ntpHosts;
4680

4781
/**
4882
Intializes the receiver with the given Hosts File path.
@@ -52,7 +86,17 @@
5286
@param filePath The full path to hosts file with wich to initialize the receiver.
5387
@return The receiver, initialized with the given hosts file.
5488
*/
55-
- (id)initWithNTPHostsFile:(NSString*)filePath;
89+
- (id)initWithNTPHostsFile:(NSString *)filePath;
90+
91+
/**
92+
Intializes the receiver with the given Hosts array.
93+
94+
If the `sharedInstnace` instance is `nil`, the receive will be set as the `sharedInstnace`.
95+
96+
@param ntpHosts The NSArray of NSString NTP hosts with wich to initialize `NetAssociation` objects.
97+
@return The receiver, initialized with the given hosts array.
98+
*/
99+
- (id)initWithNTPHosts:(NSArray *)ntpHosts;
56100

57101
///-------------------------------------
58102
/// @name Requesting Network Time

Code/NetworkClock.m

+44-9
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,24 @@ @implementation NetworkClock
3232

3333
+ (instancetype) sharedInstance {
3434
if (nil == sharedInstance) {
35-
return [NetworkClock clockNTPHostsFile:kDefaultNTPHostsFile];
35+
return [NetworkClock clockWithNTPHostsFile:kDefaultNTPHostsFile];
3636
}
3737
return sharedInstance;
3838
}
3939

40-
+ (void)setSharedInstance:(NetworkClock*)instance {
40+
+ (void)setSharedInstance:(NetworkClock *)instance {
4141
sharedInstance = instance;
4242
}
4343

44-
+ (instancetype)clockNTPHostsFile:(NSString*)filePath {
44+
+ (instancetype)clockWithNTPHostsFile:(NSString *)filePath {
4545
return [[NetworkClock alloc] initWithNTPHostsFile:filePath];
4646
}
4747

48-
- (id)initWithNTPHostsFile:(NSString*)filePath {
48+
+ (instancetype)clockWithNTPHosts:(NSArray *)ntpHosts {
49+
return [[NetworkClock alloc] initWithNTPHosts:ntpHosts];
50+
}
51+
52+
- (id)initWithNTPHostsFile:(NSString *)filePath {
4953
self = [super init];
5054
if (self) {
5155
[self setupSortDescriptors];
@@ -60,6 +64,21 @@ - (id)initWithNTPHostsFile:(NSString*)filePath {
6064
return self;
6165
}
6266

67+
- (id)initWithNTPHosts:(NSArray *)ntpHosts {
68+
self = [super init];
69+
if (self) {
70+
[self setupSortDescriptors];
71+
[self setupTimeAssociationsFromArray:ntpHosts];
72+
[self subscribeToAppBackgroundNotifications];
73+
[self subscribeToTimeAssociationNotifications];
74+
75+
if (nil == sharedInstance) {
76+
[NetworkClock setSharedInstance:self];
77+
}
78+
}
79+
return self;
80+
}
81+
6382
#pragma mark - Setup methods
6483

6584
- (void)setupSortDescriptors {
@@ -68,7 +87,7 @@ - (void)setupSortDescriptors {
6887
self.sortDescriptors = @[self.dispersionSortDescriptor];
6988
}
7089

71-
- (void)setupTimeAssociationsFromFile:(NSString*)filePath {
90+
- (void)setupTimeAssociationsFromFile:(NSString *)filePath {
7291
NSArray *ntpDomains = [self loadDomainNamesFromFile:filePath];
7392
NSDictionary *hostAddresses = [self resolveDomainAddressesFromArray:ntpDomains];
7493

@@ -83,6 +102,20 @@ - (void)setupTimeAssociationsFromFile:(NSString*)filePath {
83102
}
84103
}
85104

105+
- (void)setupTimeAssociationsFromArray:(NSArray *)ntpHosts {
106+
NSDictionary *hostAddresses = [self resolveDomainAddressesFromArray:ntpHosts];
107+
108+
// start an 'association' (network clock object) for each address.
109+
self.timeAssociations = [NSMutableArray arrayWithCapacity:48];
110+
for (NSString *server in [hostAddresses allKeys]) {
111+
NSString *ipAddress = hostAddresses[server][@"address"];
112+
NetAssociation *timeAssociation = [NetAssociation associationWithServerIpAddress:ipAddress];
113+
timeAssociation.alwaysTrust = [hostAddresses[server][@"alwaystrust"] boolValue];
114+
[self.timeAssociations addObject:timeAssociation];
115+
[timeAssociation enable]; // starts are randomized internally
116+
}
117+
}
118+
86119
- (void)subscribeToAppBackgroundNotifications {
87120
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationBack:)
88121
name:UIApplicationDidEnterBackgroundNotification
@@ -128,7 +161,7 @@ - (void) finishAssociations {
128161
}
129162

130163

131-
- (NSArray*)loadDomainNamesFromFile:(NSString*)filePath {
164+
- (NSArray *)loadDomainNamesFromFile:(NSString *)filePath {
132165
NSString *fileData = [[NSString alloc] initWithData:[[NSFileManager defaultManager]
133166
contentsAtPath:filePath]
134167
encoding:NSUTF8StringEncoding];
@@ -138,7 +171,7 @@ - (NSArray*)loadDomainNamesFromFile:(NSString*)filePath {
138171

139172
// for each NTP service domain name in the 'ntp.hosts' file : "0.pool.ntp.org" etc
140173
// resolve the IP address of the named host : "0.pool.ntp.org" --> [123.45.67.89], ...
141-
- (NSDictionary*)resolveDomainAddressesFromArray:(NSArray*)domains {
174+
- (NSDictionary *)resolveDomainAddressesFromArray:(NSArray *)domains {
142175
NSMutableDictionary *hostAddresses = [NSMutableDictionary dictionaryWithCapacity:48];
143176

144177
for (NSString * ntpDomainName in domains) {
@@ -159,7 +192,7 @@ - (NSDictionary*)resolveDomainAddressesFromArray:(NSArray*)domains {
159192
}
160193

161194
if (!CFHostStartInfoResolution (ntpHostName, kCFHostAddresses, &nameError)) {
162-
LogInProduction(@"CFHostStartInfoResolution error %li", nameError.error);
195+
LogInProduction(@"CFHostStartInfoResolution error %i", (int)nameError.error);
163196
CFRelease(ntpHostName);
164197
continue; // couldn't start resolution ...
165198
}
@@ -233,7 +266,9 @@ - (void) offsetAverage {
233266
short usefulCount = 0;
234267

235268
for (NetAssociation * timeAssociation in sortedArray) {
236-
if (timeAssociation.trusty) {
269+
BOOL isValidOffset = (timeAssociation.offset < -0.0000001 || 0.0000001 < timeAssociation.offset);
270+
BOOL isTrusty = (timeAssociation.alwaysTrust && timeAssociation.trusty);
271+
if (isValidOffset && isTrusty) {
237272
usefulCount++;
238273
self.timeIntervalSinceDeviceTime += timeAssociation.offset;
239274
}

Examples/ntpDemo/ntpDemo/AppDelegate.m

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@
1212
@implementation AppDelegate
1313

1414
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
15-
[NetworkClock clockNTPHostsFile:[[NSBundle mainBundle] pathForResource:@"ntp.hosts" ofType:@""]];
15+
[NetworkClock clockWithNTPHostsFile:[[NSBundle mainBundle] pathForResource:@"ntp.hosts" ofType:@""]];
16+
17+
// Alternatively:
18+
// [NetworkClock clockWithNTPHosts:@[
19+
// @"time.apple.com",
20+
// @"south-america.pool.ntp.org alwaystrust"
21+
// ]];
1622
return YES;
1723
}
1824

README.mdown

+74-7
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,84 @@ This project is a mirror of the iOS-ntp project hosted on [Google code](http://c
44

55
### Notes
66

7-
The structure of this project may need some cleaning up and modernisation but it is ARC compatible and runs fine on Xcode 4.6.2.
7+
The structure of this project may need some cleaning up and modernisation but it is ARC compatible and runs fine on Xcode 5.0.2.
88

99
It probably neeeds to be worked around a newer version of Cocoa Async Socket https://github.com/mattsoftware/CocoaAsyncSocket
1010

11-
### Running the project
1211

13-
git clone git://github.com/jessedc/iOS-ntp.git ios-ntp
14-
cd ios-ntp
15-
git submodule sync
16-
git submodule update --init
17-
open ntpA.xcodeproj
12+
### Install using CocoaPods
13+
14+
##### Podspec
15+
```
16+
platform :ios, '6.0'
17+
pod 'iOS-ntp', :git => 'https://github.com/tomaszrybakiewicz/iOS-ntp'
18+
```
19+
20+
### Examples
21+
22+
23+
##### AppDelegate.m
24+
25+
#import "AppDelegate.h"
26+
#import <iOS-ntp/NetworkClock.h>
27+
28+
@implementation AppDelegate
29+
30+
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
31+
-
32+
[NetworkClock clockWithNTPHosts:@[
33+
@"time.apple.com",
34+
@"south-america.pool.ntp.org alwaystrust"
35+
]];
36+
37+
return YES;
38+
}
39+
40+
@end
41+
42+
##### RootViewController.m
43+
44+
#import "ViewController.h"
45+
#import <iOS-ntp/NetworkClock.h>
46+
47+
@implementation ViewController
48+
49+
- (void)viewDidLoad {
50+
[super viewDidLoad];
51+
[self updateTime];
52+
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime) userInfo:nil repeats:YES];
53+
}
54+
55+
- (void)updateTime {
56+
NSDate *systemTime = [NSDate date];
57+
NSDate *networkTime = [[NetworkClock sharedInstance] networkTime];
58+
NSTimeInterval difference = [networkTime timeIntervalSinceDate:systemTime];
59+
60+
NSLog(@"system: %@", systemTime);
61+
NSLog(@"network: %@", networkTime);
62+
NSLog(@"difference: %5.3f sec", difference);
63+
}
64+
65+
@end
66+
67+
68+
Example iOS application can be found in Examples folder.
69+
70+
```
71+
$ cd Examples/ntpDemo/
72+
$ pod install
73+
$ open ntpDemo.xcworkspace
74+
```
75+
76+
### Running the library project
77+
78+
```
79+
$ git clone [email protected]:tomaszrybakiewicz/iOS-ntp.git ios-ntp_git
80+
$ cd ios-ntp_git
81+
$ git submodule sync
82+
$ git submodule update --init
83+
$ open iOS-ntp.xcodeproj
84+
```
1885

1986
## Original README
2087

iOS-ntp.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'iOS-ntp'
3-
s.version = '0.2.0'
3+
s.version = '0.2.1'
44
s.summary = 'This project is a mirror of the iOS-ntp project hosted on [Google code](http://code.google.com/p/ios-ntp/) by Gavin Eadie'
55
s.homepage = 'http://code.google.com/p/ios-ntp/'
66
s.author = { 'Gavin Eadie' => '' }

0 commit comments

Comments
 (0)