From eceb83d72144c2d186235e8f55aa95e241b596be Mon Sep 17 00:00:00 2001 From: Ge0rges Date: Wed, 15 May 2019 11:46:54 -0400 Subject: [PATCH] Improved sync and cleaned --- .../ConnectivityManager.h | 5 +- .../ConnectivityManager.m | 467 +++++++++--------- Synaction/Synaction/Synaction.h | 2 + Synaction/Synaction/Synaction.m | 47 +- 4 files changed, 268 insertions(+), 253 deletions(-) diff --git a/Synaction/Connectivity Manager/ConnectivityManager.h b/Synaction/Connectivity Manager/ConnectivityManager.h index a96c179..beac96a 100644 --- a/Synaction/Connectivity Manager/ConnectivityManager.h +++ b/Synaction/Connectivity Manager/ConnectivityManager.h @@ -25,10 +25,11 @@ @interface ConnectivityManager : NSObject -@property (nonatomic, assign) id _Nullable delegate; -@property (nonatomic, assign) id _Nullable synaction; +@property (nonatomic, weak) id _Nullable delegate; +@property (nonatomic, weak) id _Nullable synaction; @property (strong, nonatomic) NSMutableArray * _Nonnull allSockets; +@property (strong, nonatomic) GCDAsyncSocket * _Nullable hostSocket; @property (readonly, strong, nonatomic) NSString * _Nullable hostName; diff --git a/Synaction/Connectivity Manager/ConnectivityManager.m b/Synaction/Connectivity Manager/ConnectivityManager.m index 7f39748..da995c0 100644 --- a/Synaction/Connectivity Manager/ConnectivityManager.m +++ b/Synaction/Connectivity Manager/ConnectivityManager.m @@ -22,303 +22,312 @@ @interface ConnectivityManager () *)sockets { - NSLog(@"Sending packet to sockets"); - - // Encode Packet Data - NSMutableData *packetData = [[NSMutableData alloc] init]; - NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:packetData]; - [archiver encodeObject:packet forKey:@"packet"]; - [archiver finishEncoding]; - - // Initialize Buffer - NSMutableData *buffer = [[NSMutableData alloc] init]; - - // Fill Buffer - uint64_t headerLength = [packetData length]; - [buffer appendBytes:&headerLength length:sizeof(uint64_t)]; - [buffer appendBytes:[packetData bytes] length:[packetData length]]; - - // Write Buffer - for (GCDAsyncSocket *socket in sockets) { - [socket writeData:buffer withTimeout:-1.0 tag:0]; - } + NSLog(@"Sending packet to sockets"); + + // Encode Packet Data + NSMutableData *packetData = [[NSMutableData alloc] init]; + NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:packetData]; + [archiver encodeObject:packet forKey:@"packet"]; + [archiver finishEncoding]; + + // Initialize Buffer + NSMutableData *buffer = [[NSMutableData alloc] init]; + + // Fill Buffer + uint64_t headerLength = [packetData length]; + [buffer appendBytes:&headerLength length:sizeof(uint64_t)]; + [buffer appendBytes:[packetData bytes] length:[packetData length]]; + + // Write Buffer + for (GCDAsyncSocket *socket in sockets) { + [socket writeData:buffer withTimeout:-1.0 tag:0]; + } } - (uint64_t)parseHeader:(NSData *)data { - uint64_t headerLength = 0; - memcpy(&headerLength, [data bytes], sizeof(uint64_t)); - - return headerLength; + uint64_t headerLength = 0; + memcpy(&headerLength, [data bytes], sizeof(uint64_t)); + + return headerLength; } - (Packet *)parseBody:(NSData *)data { - NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; - Packet *packet = [unarchiver decodeObjectForKey:@"packet"]; - [unarchiver finishDecoding]; - - NSLog(@"Parsed packet."); - - return packet; + NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; + Packet *packet = [unarchiver decodeObjectForKey:@"packet"]; + [unarchiver finishDecoding]; + + return packet; } #pragma mark - NSNetServiceDelegate - (void)netServiceDidPublish:(NSNetService *)service { - NSLog(@"Bonjour Service Published: domain(%@) type(%@) name(%@) port(%i)", service.domain, service.type, service.name, (int)service.port); + NSLog(@"Bonjour Service Published: domain(%@) type(%@) name(%@) port(%i)", service.domain, service.type, service.name, (int)service.port); } - (void)netService:(NSNetService *)service didNotPublish:(NSDictionary *)errorDict { - NSLog(@"Failed to Publish Service: domain(%@) type(%@) name(%@) - %@", service.domain, service.type, service.name, errorDict); + NSLog(@"Failed to Publish Service: domain(%@) type(%@) name(%@) - %@", service.domain, service.type, service.name, errorDict); } - (void)netService:(NSNetService *)service didNotResolve:(NSDictionary *)errorDict { - [service setDelegate:nil]; - NSLog(@"Service dit not resolve. Error: %@", errorDict); + [service setDelegate:nil]; + NSLog(@"Service dit not resolve. Error: %@", errorDict); } - (void)netServiceDidResolveAddress:(NSNetService *)service { - NSLog(@"Started to resolve address for service: %@", service); - - // Connect With Service - if ([self connectWithService:service]) { - NSLog(@"Did Connect with Service: domain(%@) type(%@) name(%@) port(%i)", service.domain, service.type, service.name, (int)service.port); - - self.hostName = service.name; - - if ([self.delegate respondsToSelector:@selector(didConnectToService:)]) { - [self.delegate didConnectToService:service]; - } - - if ([self.synaction respondsToSelector:@selector(didConnectToService:)]) { - [self.synaction didConnectToService:service]; - } - - } else { - NSLog(@"Unable to Connect with Service: domain(%@) type(%@) name(%@) port(%i)", service.domain, service.type, service.name, (int)service.port); - } + NSLog(@"Started to resolve address for service: %@", service); + + // Connect With Service + if ([self connectWithService:service]) { + NSLog(@"Did Connect with Service: domain(%@) type(%@) name(%@) port(%i)", service.domain, service.type, service.name, (int)service.port); + + self.hostName = service.name; + + if (self.delegate && [self.delegate respondsToSelector:@selector(didConnectToService:)]) { + [self.delegate didConnectToService:service]; + } + + if (self.synaction && [self.synaction respondsToSelector:@selector(didConnectToService:)]) { + [self.synaction didConnectToService:service]; + } + + } else { + NSLog(@"Unable to Connect with Service: domain(%@) type(%@) name(%@) port(%i)", service.domain, service.type, service.name, (int)service.port); + } } #pragma mark NSNetServiceBrowserDelegate - (void)netServiceBrowser:(NSNetServiceBrowser *)serviceBrowser didFindService:(NSNetService *)service moreComing:(BOOL)moreComing { - // Update Services - [self.services addObject:service]; - - if(!moreComing) { - // Sort Services - [self.services sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]]; - - // Update UI - - // Connect - NSNetService *service = self.services[0]; - - // Resolve Service - [service setDelegate:self]; - [service resolveWithTimeout:10.0]; - } + // Update Services + [self.services addObject:service]; + + if(!moreComing) { + // Sort Services + [self.services sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]]; + + // Update UI + + // Connect + NSNetService *service = self.services[0]; + + // Resolve Service + [service setDelegate:self]; + [service resolveWithTimeout:10.0]; + } } - (void)netServiceBrowser:(NSNetServiceBrowser *)serviceBrowser didRemoveService:(NSNetService *)service moreComing:(BOOL)moreComing { - // Update Services - [self.services removeObject:service]; + // Update Services + [self.services removeObject:service]; } - (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)serviceBrowser { - [self stopBonjour]; - NSLog(@"Net service stopped search."); + [self stopBonjour]; + NSLog(@"Net service stopped search."); } - (void)netServiceBrowser:(NSNetServiceBrowser *)aBrowser didNotSearch:(NSDictionary *)userInfo { - [self stopBonjour]; - NSLog(@"Net service did not search: %@", userInfo); + [self stopBonjour]; + NSLog(@"Net service did not search: %@", userInfo); } #pragma mark - GCDAsyncSocketDelegate - (void)socket:(GCDAsyncSocket *)socket didAcceptNewSocket:(GCDAsyncSocket *)newSocket { - NSLog(@"Accepted New Socket from %@:%hu", [newSocket connectedHost], [newSocket connectedPort]); - - // Add socket to our array - [self.allSockets addObject:newSocket]; - - // Read Data from Socket - [newSocket readDataToLength:sizeof(uint64_t) withTimeout:-1.0 tag:0]; - - // Call Delegates - if ([self.delegate respondsToSelector:@selector(socket:didAcceptNewSocket:)]) { - [self.delegate socket:socket didAcceptNewSocket:newSocket]; - } - - if ([self.synaction respondsToSelector:@selector(socket:didAcceptNewSocket:)]) { - [self.synaction socket:socket didAcceptNewSocket:newSocket]; - } + NSLog(@"Accepted New Socket from %@:%hu", [newSocket connectedHost], [newSocket connectedPort]); + + // Add socket to our array + [self.allSockets addObject:newSocket]; + + // Read Data from Socket + [newSocket readDataToLength:sizeof(uint64_t) withTimeout:-1.0 tag:0]; + + // Call Delegates + if (self.delegate && [self.delegate respondsToSelector:@selector(socket:didAcceptNewSocket:)]) { + [self.delegate socket:socket didAcceptNewSocket:newSocket]; + } + + if (self.synaction && [self.synaction respondsToSelector:@selector(socket:didAcceptNewSocket:)]) { + [self.synaction socket:socket didAcceptNewSocket:newSocket]; + } } - (void)socket:(GCDAsyncSocket *)socket didConnectToHost:(NSString *)host port:(UInt16)port { - NSLog(@"Socket Did Connect to Host: %@ Port: %hu", host, port); - - // Start Reading - [socket readDataToLength:sizeof(uint64_t) withTimeout:-1.0 tag:0]; - - if ([self.delegate respondsToSelector:@selector(socket:didConnectToHost:port:)]) { - [self.delegate socket:socket didConnectToHost:host port:port]; - } - - if ([self.synaction respondsToSelector:@selector(socket:didConnectToHost:port:)]) { - [self.synaction socket:socket didConnectToHost:host port:port]; - } + NSLog(@"Socket did connect to Host: %@ Port: %hu", host, port); + + // Start Reading + [socket readDataToLength:sizeof(uint64_t) withTimeout:-1.0 tag:0]; + + // Set the host socket + self.hostSocket = socket; + + [self.hostSocket performBlock:^{ + [self.hostSocket enableBackgroundingOnSocket]; + }]; + + if (self.delegate && [self.delegate respondsToSelector:@selector(socket:didConnectToHost:port:)]) { + [self.delegate socket:socket didConnectToHost:host port:port]; + } + + if (self.synaction && [self.synaction respondsToSelector:@selector(socket:didConnectToHost:port:)]) { + [self.synaction socket:socket didConnectToHost:host port:port]; + } } - (void)socket:(GCDAsyncSocket *)socket didReadData:(NSData *)data withTag:(long)tag { - if (tag == 0) { - uint64_t bodyLength = [self parseHeader:data]; - [socket readDataToLength:bodyLength withTimeout:-1.0 tag:1]; - - } else if (tag == 1) { - Packet *packet = [self parseBody:data]; - - if ([self.delegate respondsToSelector:@selector(didReceivePacket:fromSocket:)]) { - [self.delegate didReceivePacket:packet fromSocket:socket]; - } - - if ([self.synaction respondsToSelector:@selector(didReceivePacket:fromSocket:)]) { - [self.synaction didReceivePacket:packet fromSocket:socket]; - } - - [socket readDataToLength:sizeof(uint64_t) withTimeout:-1 tag:0]; - } + if (tag == 0) { + uint64_t bodyLength = [self parseHeader:data]; + [socket readDataToLength:(NSUInteger)bodyLength withTimeout:-1.0 tag:1]; + + } else if (tag == 1) { + Packet *packet = [self parseBody:data]; + + if (self.delegate && [self.delegate respondsToSelector:@selector(didReceivePacket:fromSocket:)]) { + [self.delegate didReceivePacket:packet fromSocket:socket]; + } + + if (self.synaction && [self.synaction respondsToSelector:@selector(didReceivePacket:fromSocket:)]) { + [self.synaction didReceivePacket:packet fromSocket:socket]; + } + + [socket readDataToLength:sizeof(uint64_t) withTimeout:-1 tag:0]; + } } - (void)socketDidDisconnect:(GCDAsyncSocket *)socket withError:(NSError *)error { - NSLog(@"%s error: %@", __PRETTY_FUNCTION__, error); - - if (socket) { - [self.allSockets removeObject:socket]; - } - - if ([socket isEqual:self.serverSocket]) { - [self.allSockets removeAllObjects]; - } - - if ([self.delegate respondsToSelector:@selector(socketDidDisconnect:withError:)]) { - [self.delegate socketDidDisconnect:socket withError:error]; - } - - if ([self.synaction respondsToSelector:@selector(socketDidDisconnect:withError:)]) { - [self.synaction socketDidDisconnect:socket withError:error]; - } + NSLog(@"%s error: %@", __PRETTY_FUNCTION__, error); + + self.hostSocket = nil; + + if (socket) { + [self.allSockets removeObject:socket]; + } + + if ([socket isEqual:self.serverSocket]) { + [self.allSockets removeAllObjects]; + } + + if (self.delegate && [self.delegate respondsToSelector:@selector(socketDidDisconnect:withError:)]) { + [self.delegate socketDidDisconnect:socket withError:error]; + } + + if (self.synaction && [self.synaction respondsToSelector:@selector(socketDidDisconnect:withError:)]) { + [self.synaction socketDidDisconnect:socket withError:error]; + } } + @end diff --git a/Synaction/Synaction/Synaction.h b/Synaction/Synaction/Synaction.h index 6d5daf7..98525e0 100644 --- a/Synaction/Synaction/Synaction.h +++ b/Synaction/Synaction/Synaction.h @@ -12,6 +12,8 @@ // Managers #import "ConnectivityManager.h" +#define CalibrationDoneNotificationName @"CalibrationDone" + typedef void(^ _Nullable calibrationBlock)(NSArray * _Nullable peers); @interface Synaction : NSObject diff --git a/Synaction/Synaction/Synaction.m b/Synaction/Synaction/Synaction.m index 498c3e8..83b803d 100644 --- a/Synaction/Synaction/Synaction.m +++ b/Synaction/Synaction/Synaction.m @@ -34,7 +34,7 @@ + (instancetype _Nonnull)sharedManager { sharedManager.connectivityManager = [ConnectivityManager sharedManager]; sharedManager.connectivityManager.synaction = sharedManager; sharedManager.hostTimeOffset = 0; - sharedManager.maxNumberOfCalibrations = 50000; + sharedManager.maxNumberOfCalibrations = 5000; sharedManager.calibratedPeers = [NSMutableSet new]; sharedManager->calculatedOffsets = 0; sharedManager->totalCalculatedOffsets = 0; @@ -49,6 +49,7 @@ + (instancetype _Nonnull)sharedManager { - (void)executeBlockWhenAllPeersCalibrate:(NSArray * _Nonnull)peers block:(calibrationBlock)completionBlock { // Check if these peers already had the time to calibrate if (self.calibratedPeers.count >= peers.count || (peers.count > self.connectivityManager.allSockets.count && peers.count > self.calibratedPeers.count)) {// Already calibrated + NSLog(@"Executing block for all peers calibrated, already calibrated!"); completionBlock(peers); return; } @@ -212,7 +213,7 @@ - (void)atExactTime:(uint64_t)val runBlock:(dispatch_block_t _Nonnull)block { } - (void)didReceivePacket:(Packet *)packet fromSocket:(GCDAsyncSocket *)socket { - + int64_t timeReceived = (int64_t)[self currentTime]; NSDictionary *payload = [NSKeyedUnarchiver unarchiveObjectWithData:packet.data]; // Check if the host is asking us to sync @@ -223,7 +224,7 @@ - (void)didReceivePacket:(Packet *)packet fromSocket:(GCDAsyncSocket *)socket { return; } else if ([payload[@"command"] isEqualToString:@"syncDone"]) { - NSLog(@"peer told us sync done"); + NSLog(@"peer told us sync done"); [self.calibratedPeers addObject:socket]; [[NSNotificationCenter defaultCenter] postNotificationName:@"peerCalibrated" object:self.calibratedPeers userInfo:@{@"calibratedPeer": socket}]; @@ -232,7 +233,7 @@ - (void)didReceivePacket:(Packet *)packet fromSocket:(GCDAsyncSocket *)socket { } else if ([payload[@"command"] isEqualToString:@"syncPing"]) {// This is done on the peer with which we are calculating the offset (Host). NSMutableDictionary *payloadDic = [[NSMutableDictionary alloc] initWithDictionary:@{@"command": @"syncPong", - @"timeReceived": [NSNumber numberWithUnsignedLongLong:[self currentTime]], + @"timeReceived": [NSNumber numberWithUnsignedLongLong:timeReceived], @"timeSent": payload[@"timeSent"], }]; @@ -251,12 +252,13 @@ - (void)didReceivePacket:(Packet *)packet fromSocket:(GCDAsyncSocket *)socket { uint64_t timePingSent = ((NSNumber*)payload[@"timeSent"]).unsignedLongLongValue; uint64_t timeHostReceivedPing = ((NSNumber*)payload[@"timeReceived"]).unsignedLongLongValue; - //uint64_t latencyWithHost = ([self currentTime] - timePingSent)/2;// Calculates the estimated latency for one way travel + //uint64_t latencyWithHost = (timeReceived - timePingSent)/2;// Calculates the estimated latency for one way travel - // If this calculation doesn't meet our error margin, restart. - if (((int64_t)[self currentTime] - (int64_t)timePingSent) > 25000000000) { + // If this calculation doesn't meet our error margin (2s), restart. + if (((int64_t)[self currentTime] - (int64_t)timePingSent) > 2000000000) { + NSLog(@"Calibration took too long. Repeating."); NSMutableDictionary *payloadDic = [[NSMutableDictionary alloc] initWithDictionary:@{@"command": @"syncPing", - @"timeSent": [NSNumber numberWithUnsignedLongLong:[self currentTime]] + @"timeSent": [NSNumber numberWithUnsignedLongLong:timeReceived] }]; NSData *payload = [NSKeyedArchiver archivedDataWithRootObject:payloadDic]; @@ -266,29 +268,28 @@ - (void)didReceivePacket:(Packet *)packet fromSocket:(GCDAsyncSocket *)socket { return; } - int64_t calculatedOffset = ((int64_t)[self currentTime] + (int64_t)timePingSent - (2*(int64_t)timeHostReceivedPing))/2; // WAY 1. Best because it doesn't depend on latency - //calculatedOffset2 = (int64_t)latencyWithHost - (int64_t)timeHostReceivedPing + (int64_t)timePingSent;// WAY 2 - //calculatedOffset3 = -(int64_t)latencyWithHost - (int64_t)timeHostReceivedPing + (int64_t)[self currentTime];// WAY 3 - + int64_t calculatedOffset = ((int64_t)[self currentTime] + (int64_t)timePingSent - (2*(int64_t)timeHostReceivedPing))/2; + totalCalculatedOffsets += calculatedOffset; calculatedOffsets += 1; NSLog(@"Calculated calibration. Total: %f", calculatedOffsets); // If the calibration is accurate enough just end it. + BOOL doneCalibrating = false; double newOffset = totalCalculatedOffsets/calculatedOffsets; - - if (fabs(newOffset-self.hostTimeOffset) < 5000) { - self.maxNumberOfCalibrations = calculatedOffsets; - NSLog(@"prematurely ended calibration because accurate enough at value: %f", newOffset); + + if (fabs(newOffset-self.hostTimeOffset) <= 500) { + doneCalibrating = true; + NSLog(@"prematurely ended calibration because accurate enough with difference old/new: %f", fabs(newOffset-self.hostTimeOffset)); } - + NSLog(@"Got diff old/new: %f", fabs(newOffset-self.hostTimeOffset)); + self.hostTimeOffset = newOffset; - // If calculation is done notify the host. - if (calculatedOffsets >= self.maxNumberOfCalibrations) { - NSLog(@"Calibration done, informing host."); + if (calculatedOffsets >= self.maxNumberOfCalibrations || doneCalibrating) { + NSLog(@"Calibration done with maximum number of calibrations, informing host."); // Let the host know we calibrated NSMutableDictionary *payloadDic = [[NSMutableDictionary alloc] initWithDictionary:@{@"command": @"syncDone"}]; @@ -299,8 +300,10 @@ - (void)didReceivePacket:(Packet *)packet fromSocket:(GCDAsyncSocket *)socket { // Update the bool self.isCalibrating = NO; - [[NSNotificationCenter defaultCenter] postNotificationName:@"CalibrationDone" object:self]; - + + // Post the calibration done notification + [[NSNotificationCenter defaultCenter] postNotificationName:CalibrationDoneNotificationName object:self]; + } else { // Send another calibration request. NSMutableDictionary *payloadDic = [[NSMutableDictionary alloc] initWithDictionary:@{@"command": @"syncPing",