Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #import "NMASwitchPinger.h"
- /**
- * Private interface.
- */
- @interface NMASwitchPinger () {
- /**
- * The delegate of this class.
- */
- __weak id<NMASwitchPingerDelegate> delegate;
- /**
- * The timeout after which the pinger stops waiting for a response.
- */
- NSTimeInterval timeout;
- /**
- * The socket which is used to send the ping.
- */
- GCDAsyncUdpSocket *socket;
- /**
- * List of pings which are awaiting a response.
- */
- NSMutableDictionary *pendingPings;
- /**
- * Dispatch queue which serializes access to the pendingPings dictionary.
- */
- dispatch_queue_t pendingPingsAccessQueue;
- /**
- * The queue on which the delegate methods of the socket are executed.
- */
- dispatch_queue_t socketDelegateQueue;
- /**
- * Is set to true when the SwitchPinger started receiving responses (after first send)
- */
- bool receiving;
- }
- @end
- @implementation NMASwitchPinger
- #pragma mark - Initialization
- - (id)initWithTimeout:(NSTimeInterval)newTimeout delegate:(id<NMASwitchPingerDelegate>)newDelegate {
- self = [super init];
- if (self) {
- // setting passed values
- timeout = newTimeout;
- delegate = newDelegate;
- // init data structures
- pendingPings = [[NSMutableDictionary alloc] init];
- pendingPingsAccessQueue = dispatch_queue_create("de.nexans-ans.pingerPendingAccess", DISPATCH_QUEUE_SERIAL);
- // create the socket for udp sending
- socketDelegateQueue = dispatch_queue_create("de.nexans-ans.pingerDelegate", DISPATCH_QUEUE_CONCURRENT);
- socket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:socketDelegateQueue];
- }
- return self;
- }
- - (id)init {
- NSAssert(NO, @"Use the designated initializer");
- return nil;
- }
- #pragma mark - Sending a ping
- - (void)sendPingToAddress:(NSString *)address {
- // we allow only one ping at a time to the same ip
- __block BOOL alreadyInList = NO;
- dispatch_sync(pendingPingsAccessQueue, ^{
- if (pendingPings[address]) {
- alreadyInList = YES;
- } else {
- pendingPings[address] = [[NSDate alloc] init];
- }
- });
- // don't send a second ping to the same address
- if (alreadyInList) {
- NSLog(@"SimplePinger: did not send ping because already a ping pending to this addres: %@", address);
- return;
- }
- // create a minimal packet (3 bytes)
- NSMutableData *packet = [[NSMutableData alloc] initWithCapacity:3];
- uint16_t vendor_value = CFSwapInt16HostToBig(266);
- uint8_t request_type = 1;
- [packet appendBytes:&vendor_value length:sizeof(vendor_value)];
- [packet appendBytes:&request_type length:sizeof(request_type)];
- // send over the wire
- [socket sendData:packet toHost:address port:50266 withTimeout:timeout tag:0];
- // schedule timeout handler
- dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC));
- dispatch_after(popTime, pendingPingsAccessQueue, ^(void){
- [self removeTimedOutPingWithAddress:address];
- });
- // start receiving when not already receiving
- if (!receiving) {
- bool recvGood = [socket beginReceiving:nil];
- NSAssert(recvGood, @"SimplePinger: could not start receiving");
- receiving = YES;
- }
- }
- #pragma mark - GCDAsyncSocket delegate
- - (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext {
- NSString *ipAddress = [GCDAsyncUdpSocket hostFromAddress:address];
- __block BOOL pingStillPending = NO;
- dispatch_sync(pendingPingsAccessQueue, ^{
- NSDate *sendDate = pendingPings[ipAddress];
- if (sendDate) {
- [pendingPings removeObjectForKey:ipAddress];
- pingStillPending = YES;
- }
- });
- if (pingStillPending) {
- dispatch_async(dispatch_get_main_queue(), ^{
- [delegate switchPinger:self didReceiveResponse:data fromAddress:ipAddress];
- });
- }
- }
- - (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error {
- NSLog(@"didnt send data");
- }
- - (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag {
- NSLog(@"did send");
- }
- #pragma mark - Private methods
- /**
- * Removes a timed out ping. A call of this function gets scheduled when a ping is send.
- *
- * @param address The address of the ping which should be removed.
- */
- - (void)removeTimedOutPingWithAddress:(NSString*)address {
- NSDate *sendDate = pendingPings[address];
- if (sendDate) {
- NSLog(@"timeout: %@", address);
- NSAssert(fabs([sendDate timeIntervalSinceNow]) >= timeout, @"SimplePing: removed ping before timout");
- [pendingPings removeObjectForKey:address];
- dispatch_async(dispatch_get_main_queue(), ^{
- [delegate switchPinger:self didReceiveTimeoutFromAddress:address];
- });
- }
- }
- @end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement