This question I asked on stackoverflow.com goes into more detail, and I got some helpful answers: http://stackoverflow.com/questions/3755208/measuring-time-difference-between-networked-devices
This wikipedia page was also helpful:
http://en.wikipedia.org/wiki/Precision_Time_Protocol
So, with all that info, I made a class to do the job that I'm fairly proud of. Perhaps it will be useful to someone else someday. Do whatever you want with the code, but it would be fun if you left a comment to say it was useful. The actual network communication stuff obviously needs to be changed to fit in your code.
TimeSync.h
//Call [timeSync startSync] on the client. The offset variable will be continually updated
//throughout the life of the program, and it will be sent back to the server also.
#define TS_DELAY_MAX 10000
@interface TimeSync : NSObject
{
NSTimeInterval t1, t2; //t1 is client time - server time, and t2 is server time - client time
NSTimeInterval offset; //this doesn't get calculated from t1 and t2 until it's stabilized
uint synccount;
}
-(NSTimeInterval)offset;
-(void)checkSyncStatus;
-(void)sendTimeSync;
-(void)sendDelayRequest;
-(void)sendDelayResponse:(NSTimeInterval)from;
-(void)processPacketType:(uint8_t)type TimeStamp:(NSTimeInterval)timeStamp;
-(void)sendSyncRequest;
-(void)publishOffset;
TimeSync.m
@implementation TimeSync
-(id)init
{
if (self = [super init])
{
t1 = TS_DELAY_MAX;
t2 = TS_DELAY_MAX;
offset = TS_DELAY_MAX;
synccount = 0;
}
return self;
}
-(void)sendSyncRequest
{
[[Basket shared].connection sendPacket:PT_SYNC_REQ Data:nil Mode:GKSendDataReliable];
}
-(void)sendTimeSync
{
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
[[Basket shared].connection sendPacket:PT_TIME_SYNC Data:[NSData dataWithBytes:&now length:sizeof(NSTimeInterval)] Mode:GKSendDataUnreliable];
}
-(void)sendDelayRequest
{
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
[[Basket shared].connection sendPacket:PT_DELAY_REQ Data:[NSData dataWithBytes:&now length:sizeof(NSTimeInterval)] Mode:GKSendDataUnreliable];
}
-(void)sendDelayResponse:(NSTimeInterval)from
{
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval diff = now - from;
[[Basket shared].connection sendPacket:PT_DELAY_RSP Data:[NSData dataWithBytes:&diff length:sizeof(NSTimeInterval)] Mode:GKSendDataUnreliable];
}
-(void)sendSyncNotify
{
[[Basket shared].connection sendPacket:PT_SYNC_NOTIFY Data:[NSData dataWithBytes:&offset length:sizeof(NSTimeInterval)] Mode:GKSendDataReliable];
}
-(void)startSync
{
//get a first quick measurement, then only measure every 10 seconds
//basically, a new offset will be published every minute
if (offset == TS_DELAY_MAX)
{
[self performSelector:@selector(startSync) withObject:nil afterDelay:0.5];
}
else
{
[self performSelector:@selector(startSync) withObject:nil afterDelay:10.0];
}
[self sendSyncRequest];
}
-(void)publishOffset
{
offset = (t2 - t1)/2.0;
NSLog(@"TimeSync found delay to be %f", offset);
NSLog(@"Ping was %f", (t2 + t1)/2.0);
//reset so we can measure a new drift
t1 = TS_DELAY_MAX;
t2 = TS_DELAY_MAX;
NSLog(@"Notifying server of offset...");
[self sendSyncNotify];
}
-(void)processPacketType:(uint8_t)type TimeStamp:(NSTimeInterval)timeStamp
{
NSTimeInterval timediff;
switch (type)
{
case PT_SYNC_REQ:
[self sendTimeSync];
break;
case PT_TIME_SYNC:
timediff = [NSDate timeIntervalSinceReferenceDate] - timeStamp;
if (timediff < t1)
t1 = timediff;
[self sendDelayRequest];
break;
case PT_DELAY_REQ:
[self sendDelayResponse:timeStamp];
break;
case PT_DELAY_RSP:
if (timeStamp < t2)
t2 = timeStamp;
synccount++;
if (synccount % 5 == 0) //publish the new offset every 5 measurements
{
[self publishOffset];
}
break;
case PT_SYNC_NOTIFY:
offset = -timeStamp;
NSLog(@"Received notification of offset:%f", offset);
default:
break;
}
}
-(NSTimeInterval)offset
{
return offset;
}
1 comment:
Great blog and very useful PTP wiki link.
I have been looking at mobile device fingerprinting for a payments solution and investigated whether mobile-server clock offset would be meaningful and stay reasonably consistent over days/weeks
I wrote a small script to measure 20 round trip requests, discards a handful of the longest journeys then discards the outlier clock offsets. The remaining offsets are then averaged.
The script is at http://sandboxing.net/sandbox/deviceFingerprint/
I borrowed many of the device fingerprinting concepts from the publically published https://panopticlick.eff.org
Mobile-server clock offest turned out to be very consistent in the short term, even when comparing WiFi, 3G, Edge and 2G where the round trips would vary considerably
Although over time the clocks would drift by seconds, which made the clock offset feature unusable when comparing mobile visits separated by days/weeks
One of the possible sources of this drift is the automatic clock setting on most mobiles, which may or may not be enabled
Post a Comment