diff --git a/PingTester.xcodeproj/project.pbxproj b/PingTester.xcodeproj/project.pbxproj index ce991bf..c079c36 100644 --- a/PingTester.xcodeproj/project.pbxproj +++ b/PingTester.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - 8D4CB64814C6754200875077 /* SimplePingHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D4CB64714C6754200875077 /* SimplePingHelper.m */; }; + 8D4CB64814C6754200875077 /* SimplePingHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D4CB64714C6754200875077 /* SimplePingHelper.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 8D54207F14C650C600B3689B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D54207E14C650C600B3689B /* UIKit.framework */; }; 8D54208114C650C600B3689B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D54208014C650C600B3689B /* Foundation.framework */; }; 8D54208314C650C600B3689B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D54208214C650C600B3689B /* CoreGraphics.framework */; }; @@ -19,7 +19,7 @@ 8D5420AC14C651E700B3689B /* Radio-Tower@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8D5420A914C651E700B3689B /* Radio-Tower@2x.png */; }; 8D5420AD14C651E700B3689B /* Radio-Tower.png in Resources */ = {isa = PBXBuildFile; fileRef = 8D5420AA14C651E700B3689B /* Radio-Tower.png */; }; 8D5420AE14C651E700B3689B /* PP Icons.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8D5420AB14C651E700B3689B /* PP Icons.txt */; }; - 8D5420B714C6681000B3689B /* SimplePing.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D5420B614C6681000B3689B /* SimplePing.m */; }; + 8D5420B714C6681000B3689B /* SimplePing.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D5420B614C6681000B3689B /* SimplePing.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 8D5420B914C66A3000B3689B /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D5420B814C66A3000B3689B /* SystemConfiguration.framework */; }; 8D5420BB14C66A5D00B3689B /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D5420BA14C66A5D00B3689B /* CFNetwork.framework */; }; /* End PBXBuildFile section */ diff --git a/PingTester/FirstViewController.m b/PingTester/FirstViewController.m index 54fdbd3..cfde46b 100644 --- a/PingTester/FirstViewController.m +++ b/PingTester/FirstViewController.m @@ -47,7 +47,23 @@ - (void)tapPing { [self log:@""]; [self log:@"-----------"]; [self log:@"Tapped Ping"]; - [SimplePingHelper ping:self.ipAddr.text target:self sel:@selector(pingResult:)]; +// [SimplePingHelper ping:self.ipAddr.text target:self sel:@selector(pingResult:)]; + + + [SimplePingHelper ping:@"www.baidu.com" + callback:^(NSNumber *b){ + if(b.boolValue) + { + NSLog(@"%@", @"success"); + [self log:@"success"]; + + } + else + { + NSLog(@"%@", @"failure"); + [self log:@"failure"]; + } + }]; } - (void)pingResult:(NSNumber*)success { diff --git a/PingTester/SimplePing.h b/PingTester/SimplePing.h index 31c57a4..f160011 100755 --- a/PingTester/SimplePing.h +++ b/PingTester/SimplePing.h @@ -5,7 +5,7 @@ Written by: DTS - Copyright: Copyright (c) 2010 Apple Inc. All Rights Reserved. + Copyright: Copyright (c) 2010-2012 Apple Inc. All Rights Reserved. Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") in consideration of your agreement to the following @@ -32,7 +32,7 @@ patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated. - The Apple Software is provided by Apple on an "AS IS" basis. + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING @@ -54,7 +54,7 @@ #import #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR - #import +#import #else #import #endif @@ -68,46 +68,36 @@ @protocol SimplePingDelegate; @interface SimplePing : NSObject -{ - NSString * _hostName; - NSData * _hostAddress; - CFHostRef _host; - CFSocketRef _socket; - - id _delegate; - uint16_t _identifier; // host byte order - uint16_t _nextSequenceNumber; // host byte order -} + (SimplePing *)simplePingWithHostName:(NSString *)hostName; // chooses first IPv4 address + (SimplePing *)simplePingWithHostAddress:(NSData *)hostAddress; // contains (struct sockaddr) -@property (nonatomic, assign, readwrite) id delegate; +@property (nonatomic, weak, readwrite) id delegate; -@property (nonatomic, copy, readonly) NSString * hostName; -@property (nonatomic, copy, readonly) NSData * hostAddress; -@property (nonatomic, assign, readonly) uint16_t identifier; -@property (nonatomic, assign, readonly) uint16_t nextSequenceNumber; +@property (nonatomic, copy, readonly ) NSString * hostName; +@property (nonatomic, copy, readonly ) NSData * hostAddress; +@property (nonatomic, assign, readonly ) uint16_t identifier; +@property (nonatomic, assign, readonly ) uint16_t nextSequenceNumber; - (void)start; - // Starts the pinger object pinging. You should call this after - // you've setup the delegate and any ping parameters. +// Starts the pinger object pinging. You should call this after +// you've setup the delegate and any ping parameters. - (void)sendPingWithData:(NSData *)data; - // Sends an actual ping. Pass nil for data to use a standard 56 byte payload (resulting in a - // standard 64 byte ping). Otherwise pass a non-nil value and it will be appended to the - // ICMP header. - // - // Do not try to send a ping before you receive the -simplePing:didStartWithAddress: delegate - // callback. +// Sends an actual ping. Pass nil for data to use a standard 56 byte payload (resulting in a +// standard 64 byte ping). Otherwise pass a non-nil value and it will be appended to the +// ICMP header. +// +// Do not try to send a ping before you receive the -simplePing:didStartWithAddress: delegate +// callback. - (void)stop; - // Stops the pinger object. You should call this when you're done - // pinging. +// Stops the pinger object. You should call this when you're done +// pinging. + (const struct ICMPHeader *)icmpInPacket:(NSData *)packet; - // Given a valid IP packet contains an ICMP , returns the address of the ICMP header that - // follows the IP header. This doesn't do any significant validation of the packet. +// Given a valid IP packet contains an ICMP , returns the address of the ICMP header that +// follows the IP header. This doesn't do any significant validation of the packet. @end @@ -116,32 +106,32 @@ @optional - (void)simplePing:(SimplePing *)pinger didStartWithAddress:(NSData *)address; - // Called after the SimplePing has successfully started up. After this callback, you - // can start sending pings via -sendPingWithData: - +// Called after the SimplePing has successfully started up. After this callback, you +// can start sending pings via -sendPingWithData: + - (void)simplePing:(SimplePing *)pinger didFailWithError:(NSError *)error; - // If this is called, the SimplePing object has failed. By the time this callback is - // called, the object has stopped (that is, you don't need to call -stop yourself). +// If this is called, the SimplePing object has failed. By the time this callback is +// called, the object has stopped (that is, you don't need to call -stop yourself). -// IMPORTANT: On the send side the packet does not include an IP header. -// On the receive side, it does. In that case, use +[SimplePing icmpInPacket:] +// IMPORTANT: On the send side the packet does not include an IP header. +// On the receive side, it does. In that case, use +[SimplePing icmpInPacket:] // to find the ICMP header within the packet. - (void)simplePing:(SimplePing *)pinger didSendPacket:(NSData *)packet; - // Called whenever the SimplePing object has successfully sent a ping packet. - +// Called whenever the SimplePing object has successfully sent a ping packet. + - (void)simplePing:(SimplePing *)pinger didFailToSendPacket:(NSData *)packet error:(NSError *)error; - // Called whenever the SimplePing object tries and fails to send a ping packet. +// Called whenever the SimplePing object tries and fails to send a ping packet. - (void)simplePing:(SimplePing *)pinger didReceivePingResponsePacket:(NSData *)packet; - // Called whenever the SimplePing object receives an ICMP packet that looks like - // a response to one of our pings (that is, has a valid ICMP checksum, has - // an identifier that matches our identifier, and has a sequence number in - // the range of sequence numbers that we've sent out). +// Called whenever the SimplePing object receives an ICMP packet that looks like +// a response to one of our pings (that is, has a valid ICMP checksum, has +// an identifier that matches our identifier, and has a sequence number in +// the range of sequence numbers that we've sent out). - (void)simplePing:(SimplePing *)pinger didReceiveUnexpectedPacket:(NSData *)packet; - // Called whenever the SimplePing object receives an ICMP packet that does not - // look like a response to one of our pings. +// Called whenever the SimplePing object receives an ICMP packet that does not +// look like a response to one of our pings. @end @@ -152,18 +142,18 @@ // IP header structure: struct IPHeader { - uint8_t versionAndHeaderLength; - uint8_t differentiatedServices; - uint16_t totalLength; - uint16_t identification; - uint16_t flagsAndFragmentOffset; - uint8_t timeToLive; - uint8_t protocol; - uint16_t headerChecksum; - uint8_t sourceAddress[4]; - uint8_t destinationAddress[4]; - // options... - // data... + uint8_t versionAndHeaderLength; + uint8_t differentiatedServices; + uint16_t totalLength; + uint16_t identification; + uint16_t flagsAndFragmentOffset; + uint8_t timeToLive; + uint8_t protocol; + uint16_t headerChecksum; + uint8_t sourceAddress[4]; + uint8_t destinationAddress[4]; + // options... + // data... }; typedef struct IPHeader IPHeader; @@ -182,19 +172,19 @@ check_compile_time(offsetof(IPHeader, destinationAddress) == 16); // ICMP type and code combinations: enum { - kICMPTypeEchoReply = 0, // code is always 0 - kICMPTypeEchoRequest = 8 // code is always 0 + kICMPTypeEchoReply = 0, // code is always 0 + kICMPTypeEchoRequest = 8 // code is always 0 }; // ICMP header structure: struct ICMPHeader { - uint8_t type; - uint8_t code; - uint16_t checksum; - uint16_t identifier; - uint16_t sequenceNumber; - // data... + uint8_t type; + uint8_t code; + uint16_t checksum; + uint16_t identifier; + uint16_t sequenceNumber; + // data... }; typedef struct ICMPHeader ICMPHeader; diff --git a/PingTester/SimplePing.m b/PingTester/SimplePing.m index a532926..cc30ffd 100755 --- a/PingTester/SimplePing.m +++ b/PingTester/SimplePing.m @@ -5,7 +5,7 @@ Written by: DTS - Copyright: Copyright (c) 2010 Apple Inc. All Rights Reserved. + Copyright: Copyright (c) 2010-2012 Apple Inc. All Rights Reserved. Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") in consideration of your agreement to the following @@ -32,7 +32,7 @@ original Apple software (the "Apple Software"), to use, patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated. - The Apple Software is provided by Apple on an "AS IS" basis. + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING @@ -60,45 +60,45 @@ INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED #pragma mark * ICMP On-The-Wire Format static uint16_t in_cksum(const void *buffer, size_t bufferLen) - // This is the standard BSD checksum code, modified to use modern types. +// This is the standard BSD checksum code, modified to use modern types. { - size_t bytesLeft; - int32_t sum; - const uint16_t * cursor; - union { - uint16_t us; - uint8_t uc[2]; - } last; - uint16_t answer; - - bytesLeft = bufferLen; - sum = 0; - cursor = buffer; - - /* - * Our algorithm is simple, using a 32 bit accumulator (sum), we add - * sequential 16 bit words to it, and at the end, fold back all the - * carry bits from the top 16 bits into the lower 16 bits. - */ - while (bytesLeft > 1) { - sum += *cursor; - cursor += 1; - bytesLeft -= 2; - } - - /* mop up an odd byte, if necessary */ - if (bytesLeft == 1) { - last.uc[0] = * (const uint8_t *) cursor; - last.uc[1] = 0; - sum += last.us; - } - - /* add back carry outs from top 16 bits to low 16 bits */ - sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ - sum += (sum >> 16); /* add carry */ - answer = ~sum; /* truncate to 16 bits */ - - return answer; + size_t bytesLeft; + int32_t sum; + const uint16_t * cursor; + union { + uint16_t us; + uint8_t uc[2]; + } last; + uint16_t answer; + + bytesLeft = bufferLen; + sum = 0; + cursor = buffer; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), we add + * sequential 16 bit words to it, and at the end, fold back all the + * carry bits from the top 16 bits into the lower 16 bits. + */ + while (bytesLeft > 1) { + sum += *cursor; + cursor += 1; + bytesLeft -= 2; + } + + /* mop up an odd byte, if necessary */ + if (bytesLeft == 1) { + last.uc[0] = * (const uint8_t *) cursor; + last.uc[1] = 0; + sum += last.us; + } + + /* add back carry outs from top 16 bits to low 16 bits */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = (uint16_t) ~sum; /* truncate to 16 bits */ + + return answer; } #pragma mark * SimplePing @@ -108,513 +108,520 @@ @interface SimplePing () @property (nonatomic, copy, readwrite) NSData * hostAddress; @property (nonatomic, assign, readwrite) uint16_t nextSequenceNumber; -- (void)_stopHostResolution; -- (void)_stopDataTransfer; +- (void)stopHostResolution; +- (void)stopDataTransfer; @end @implementation SimplePing - -- (id)initWithHostName:(NSString *)hostName address:(NSData *)hostAddress - // The initialiser common to both of our construction class methods. { - assert( (hostName != nil) == (hostAddress == nil) ); - self = [super init]; - if (self != nil) { - self->_hostName = [hostName copy]; - self->_hostAddress = [hostAddress copy]; - self->_identifier = (uint16_t) arc4random(); - } - return self; + CFHostRef _host; + CFSocketRef _socket; } -- (void)dealloc { - // -stop takes care of _host and _socket. +@synthesize hostName = _hostName; +@synthesize hostAddress = _hostAddress; - [self stop]; - assert(self->_host == NULL); - assert(self->_socket == NULL); - - [self->_hostName release]; - [self->_hostAddress release]; +@synthesize delegate = _delegate; +@synthesize identifier = _identifier; +@synthesize nextSequenceNumber = _nextSequenceNumber; - [super dealloc]; +- (id)initWithHostName:(NSString *)hostName address:(NSData *)hostAddress +// The initialiser common to both of our construction class methods. +{ + assert( (hostName != nil) == (hostAddress == nil) ); + self = [super init]; + if (self != nil) { + self->_hostName = [hostName copy]; + self->_hostAddress = [hostAddress copy]; + self->_identifier = (uint16_t) arc4random(); + } + return self; +} + +- (void)dealloc +{ + // -stop takes care of _host and _socket. + [self stop]; + assert(self->_host == NULL); + assert(self->_socket == NULL); } + (SimplePing *)simplePingWithHostName:(NSString *)hostName - // See comment in header. +// See comment in header. { - return [[[SimplePing alloc] initWithHostName:hostName address:nil] autorelease]; + return [[SimplePing alloc] initWithHostName:hostName address:nil]; } + (SimplePing *)simplePingWithHostAddress:(NSData *)hostAddress - // See comment in header. +// See comment in header. { - return [[[SimplePing alloc] initWithHostName:NULL address:hostAddress] autorelease]; + return [[SimplePing alloc] initWithHostName:NULL address:hostAddress]; } -@synthesize hostName = _hostName; -@synthesize hostAddress = _hostAddress; - -@synthesize delegate = _delegate; -@synthesize identifier = _identifier; -@synthesize nextSequenceNumber = _nextSequenceNumber; +- (void)noop +{ +} -- (void)_didFailWithError:(NSError *)error - // Shut down the pinger object and tell the delegate about the error. +- (void)didFailWithError:(NSError *)error +// Shut down the pinger object and tell the delegate about the error. { - assert(error != nil); - - // We retain ourselves temporarily because it's common for the delegate method - // to release its last reference to use, which causes -dealloc to be called here. - // If we then reference self on the return path, things go badly. I don't think - // that happens currently, but I've got into the habit of doing this as a - // defensive measure. - - [[self retain] autorelease]; - - [self stop]; - if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(simplePing:didFailWithError:)] ) { - [self.delegate simplePing:self didFailWithError:error]; - } + assert(error != nil); + + // We retain ourselves temporarily because it's common for the delegate method + // to release its last reference to use, which causes -dealloc to be called here. + // If we then reference self on the return path, things go badly. I don't think + // that happens currently, but I've got into the habit of doing this as a + // defensive measure. + + [self performSelector:@selector(noop) withObject:nil afterDelay:0.0]; + + [self stop]; + if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(simplePing:didFailWithError:)] ) { + [self.delegate simplePing:self didFailWithError:error]; + } } -- (void)_didFailWithHostStreamError:(CFStreamError)streamError - // Convert the CFStreamError to an NSError and then call through to - // -_didFailWithError: to shut down the pinger object and tell the - // delegate about the error. +- (void)didFailWithHostStreamError:(CFStreamError)streamError +// Convert the CFStreamError to an NSError and then call through to +// -didFailWithError: to shut down the pinger object and tell the +// delegate about the error. { - NSDictionary * userInfo; - NSError * error; + NSDictionary * userInfo; + NSError * error; - if (streamError.domain == kCFStreamErrorDomainNetDB) { - userInfo = [NSDictionary dictionaryWithObjectsAndKeys: + if (streamError.domain == kCFStreamErrorDomainNetDB) { + userInfo = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInteger:streamError.error], kCFGetAddrInfoFailureKey, nil - ]; - } else { - userInfo = nil; - } - error = [NSError errorWithDomain:(NSString *)kCFErrorDomainCFNetwork code:kCFHostErrorUnknown userInfo:userInfo]; - assert(error != nil); - - [self _didFailWithError:error]; + ]; + } else { + userInfo = nil; + } + error = [NSError errorWithDomain:(NSString *)kCFErrorDomainCFNetwork code:kCFHostErrorUnknown userInfo:userInfo]; + assert(error != nil); + + [self didFailWithError:error]; } - (void)sendPingWithData:(NSData *)data - // See comment in header. +// See comment in header. { - int err; - NSData * payload; - NSMutableData * packet; - ICMPHeader * icmpPtr; - ssize_t bytesSent; - - // Construct the ping packet. - - payload = data; - if (payload == nil) { - payload = [[NSString stringWithFormat:@"%28zd bottles of beer on the wall", (ssize_t) 99 - (size_t) (self.nextSequenceNumber % 100) ] dataUsingEncoding:NSASCIIStringEncoding]; - assert(payload != nil); - - assert([payload length] == 56); - } - - packet = [NSMutableData dataWithLength:sizeof(*icmpPtr) + [payload length]]; - assert(packet != nil); - - icmpPtr = [packet mutableBytes]; - icmpPtr->type = kICMPTypeEchoRequest; - icmpPtr->code = 0; - icmpPtr->checksum = 0; - icmpPtr->identifier = OSSwapHostToBigInt16(self.identifier); - icmpPtr->sequenceNumber = OSSwapHostToBigInt16(self.nextSequenceNumber); - memcpy(&icmpPtr[1], [payload bytes], [payload length]); - - // The IP checksum returns a 16-bit number that's already in correct byte order - // (due to wacky 1's complement maths), so we just put it into the packet as a - // 16-bit unit. - - icmpPtr->checksum = in_cksum([packet bytes], [packet length]); - - // Send the packet. - - if (self->_socket == NULL) { - bytesSent = -1; - err = EBADF; - } else { - bytesSent = sendto( + int err; + NSData * payload; + NSMutableData * packet; + ICMPHeader * icmpPtr; + ssize_t bytesSent; + + // Construct the ping packet. + + payload = data; + if (payload == nil) { + payload = [[NSString stringWithFormat:@"%28zd bottles of beer on the wall", (ssize_t) 99 - (size_t) (self.nextSequenceNumber % 100) ] dataUsingEncoding:NSASCIIStringEncoding]; + assert(payload != nil); + + assert([payload length] == 56); + } + + packet = [NSMutableData dataWithLength:sizeof(*icmpPtr) + [payload length]]; + assert(packet != nil); + + icmpPtr = [packet mutableBytes]; + icmpPtr->type = kICMPTypeEchoRequest; + icmpPtr->code = 0; + icmpPtr->checksum = 0; + icmpPtr->identifier = OSSwapHostToBigInt16(self.identifier); + icmpPtr->sequenceNumber = OSSwapHostToBigInt16(self.nextSequenceNumber); + memcpy(&icmpPtr[1], [payload bytes], [payload length]); + + // The IP checksum returns a 16-bit number that's already in correct byte order + // (due to wacky 1's complement maths), so we just put it into the packet as a + // 16-bit unit. + + icmpPtr->checksum = in_cksum([packet bytes], [packet length]); + + // Send the packet. + + if (self->_socket == NULL) { + bytesSent = -1; + err = EBADF; + } else { + bytesSent = sendto( CFSocketGetNative(self->_socket), [packet bytes], - [packet length], - 0, - (struct sockaddr *) [self.hostAddress bytes], + [packet length], + 0, + (struct sockaddr *) [self.hostAddress bytes], (socklen_t) [self.hostAddress length] - ); - err = 0; - if (bytesSent < 0) { - err = errno; - } + ); + err = 0; + if (bytesSent < 0) { + err = errno; } + } - // Handle the results of the send. - - if ( (bytesSent > 0) && (((NSUInteger) bytesSent) == [packet length]) ) { + // Handle the results of the send. - // Complete success. Tell the client. + if ( (bytesSent > 0) && (((NSUInteger) bytesSent) == [packet length]) ) { - if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(simplePing:didSendPacket:)] ) { - [self.delegate simplePing:self didSendPacket:packet]; - } - } else { - NSError * error; - - // Some sort of failure. Tell the client. - - if (err == 0) { - err = ENOBUFS; // This is not a hugely descriptor error, alas. - } - error = [NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]; - if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(simplePing:didFailToSendPacket:error:)] ) { - [self.delegate simplePing:self didFailToSendPacket:packet error:error]; - } + // Complete success. Tell the client. + + if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(simplePing:didSendPacket:)] ) { + [self.delegate simplePing:self didSendPacket:packet]; } - - self.nextSequenceNumber += 1; + } else { + NSError * error; + + // Some sort of failure. Tell the client. + + if (err == 0) { + err = ENOBUFS; // This is not a hugely descriptor error, alas. + } + error = [NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]; + if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(simplePing:didFailToSendPacket:error:)] ) { + [self.delegate simplePing:self didFailToSendPacket:packet error:error]; + } + } + + self.nextSequenceNumber += 1; } -+ (NSUInteger)_icmpHeaderOffsetInPacket:(NSData *)packet - // Returns the offset of the ICMPHeader within an IP packet. ++ (NSUInteger)icmpHeaderOffsetInPacket:(NSData *)packet +// Returns the offset of the ICMPHeader within an IP packet. { - NSUInteger result; - const struct IPHeader * ipPtr; - size_t ipHeaderLength; - - result = NSNotFound; - if ([packet length] >= (sizeof(IPHeader) + sizeof(ICMPHeader))) { - ipPtr = (const IPHeader *) [packet bytes]; - assert((ipPtr->versionAndHeaderLength & 0xF0) == 0x40); // IPv4 - assert(ipPtr->protocol == 1); // ICMP - ipHeaderLength = (ipPtr->versionAndHeaderLength & 0x0F) * sizeof(uint32_t); - if ([packet length] >= (ipHeaderLength + sizeof(ICMPHeader))) { - result = ipHeaderLength; - } + NSUInteger result; + const struct IPHeader * ipPtr; + size_t ipHeaderLength; + + result = NSNotFound; + if ([packet length] >= (sizeof(IPHeader) + sizeof(ICMPHeader))) { + ipPtr = (const IPHeader *) [packet bytes]; + assert((ipPtr->versionAndHeaderLength & 0xF0) == 0x40); // IPv4 + assert(ipPtr->protocol == 1); // ICMP + ipHeaderLength = (ipPtr->versionAndHeaderLength & 0x0F) * sizeof(uint32_t); + if ([packet length] >= (ipHeaderLength + sizeof(ICMPHeader))) { + result = ipHeaderLength; } - return result; + } + return result; } + (const struct ICMPHeader *)icmpInPacket:(NSData *)packet - // See comment in header. +// See comment in header. { - const struct ICMPHeader * result; - NSUInteger icmpHeaderOffset; - - result = nil; - icmpHeaderOffset = [self _icmpHeaderOffsetInPacket:packet]; - if (icmpHeaderOffset != NSNotFound) { - result = (const struct ICMPHeader *) (((const uint8_t *)[packet bytes]) + icmpHeaderOffset); - } - return result; + const struct ICMPHeader * result; + NSUInteger icmpHeaderOffset; + + result = nil; + icmpHeaderOffset = [self icmpHeaderOffsetInPacket:packet]; + if (icmpHeaderOffset != NSNotFound) { + result = (const struct ICMPHeader *) (((const uint8_t *)[packet bytes]) + icmpHeaderOffset); + } + return result; } -- (BOOL)_isValidPingResponsePacket:(NSMutableData *)packet - // Returns true if the packet looks like a valid ping response packet destined - // for us. +- (BOOL)isValidPingResponsePacket:(NSMutableData *)packet +// Returns true if the packet looks like a valid ping response packet destined +// for us. { - BOOL result; - NSUInteger icmpHeaderOffset; - ICMPHeader * icmpPtr; - uint16_t receivedChecksum; - uint16_t calculatedChecksum; - - result = NO; - - icmpHeaderOffset = [[self class] _icmpHeaderOffsetInPacket:packet]; - if (icmpHeaderOffset != NSNotFound) { - icmpPtr = (struct ICMPHeader *) (((uint8_t *)[packet mutableBytes]) + icmpHeaderOffset); - - receivedChecksum = icmpPtr->checksum; - icmpPtr->checksum = 0; - calculatedChecksum = in_cksum(icmpPtr, [packet length] - icmpHeaderOffset); - icmpPtr->checksum = receivedChecksum; - - if (receivedChecksum == calculatedChecksum) { - if ( (icmpPtr->type == kICMPTypeEchoReply) && (icmpPtr->code == 0) ) { - if ( OSSwapBigToHostInt16(icmpPtr->identifier) == self.identifier ) { - if ( OSSwapBigToHostInt16(icmpPtr->sequenceNumber) < self.nextSequenceNumber ) { - result = YES; - } - } - } + BOOL result; + NSUInteger icmpHeaderOffset; + ICMPHeader * icmpPtr; + uint16_t receivedChecksum; + uint16_t calculatedChecksum; + + result = NO; + + icmpHeaderOffset = [[self class] icmpHeaderOffsetInPacket:packet]; + if (icmpHeaderOffset != NSNotFound) { + icmpPtr = (struct ICMPHeader *) (((uint8_t *)[packet mutableBytes]) + icmpHeaderOffset); + + receivedChecksum = icmpPtr->checksum; + icmpPtr->checksum = 0; + calculatedChecksum = in_cksum(icmpPtr, [packet length] - icmpHeaderOffset); + icmpPtr->checksum = receivedChecksum; + + if (receivedChecksum == calculatedChecksum) { + if ( (icmpPtr->type == kICMPTypeEchoReply) && (icmpPtr->code == 0) ) { + if ( OSSwapBigToHostInt16(icmpPtr->identifier) == self.identifier ) { + if ( OSSwapBigToHostInt16(icmpPtr->sequenceNumber) < self.nextSequenceNumber ) { + result = YES; + } } + } } + } - return result; + return result; } -- (void)_readData - // Called by the socket handling code (SocketReadCallback) to process an ICMP - // messages waiting on the socket. +- (void)readData +// Called by the socket handling code (SocketReadCallback) to process an ICMP +// messages waiting on the socket. { - int err; - struct sockaddr_storage addr; - socklen_t addrLen; - ssize_t bytesRead; - void * buffer; - enum { kBufferSize = 65535 }; - - // 65535 is the maximum IP packet size, which seems like a reasonable bound - // here (plus it's what uses). - - buffer = malloc(kBufferSize); - assert(buffer != NULL); - - // Actually read the data. - - addrLen = sizeof(addr); - bytesRead = recvfrom(CFSocketGetNative(self->_socket), buffer, kBufferSize, 0, (struct sockaddr *) &addr, &addrLen); - err = 0; - if (bytesRead < 0) { - err = errno; - } - - // Process the data we read. - - if (bytesRead > 0) { - NSMutableData * packet; - - packet = [NSMutableData dataWithBytes:buffer length:bytesRead]; - assert(packet != nil); - - // We got some data, pass it up to our client. - - if ( [self _isValidPingResponsePacket:packet] ) { - if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(simplePing:didReceivePingResponsePacket:)] ) { - [self.delegate simplePing:self didReceivePingResponsePacket:packet]; - } - } else { - if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(simplePing:didReceiveUnexpectedPacket:)] ) { - [self.delegate simplePing:self didReceiveUnexpectedPacket:packet]; - } - } + int err; + struct sockaddr_storage addr; + socklen_t addrLen; + ssize_t bytesRead; + void * buffer; + enum { kBufferSize = 65535 }; + + // 65535 is the maximum IP packet size, which seems like a reasonable bound + // here (plus it's what uses). + + buffer = malloc(kBufferSize); + assert(buffer != NULL); + + // Actually read the data. + + addrLen = sizeof(addr); + bytesRead = recvfrom(CFSocketGetNative(self->_socket), buffer, kBufferSize, 0, (struct sockaddr *) &addr, &addrLen); + err = 0; + if (bytesRead < 0) { + err = errno; + } + + // Process the data we read. + + if (bytesRead > 0) { + NSMutableData * packet; + + packet = [NSMutableData dataWithBytes:buffer length:(NSUInteger) bytesRead]; + assert(packet != nil); + + // We got some data, pass it up to our client. + + if ( [self isValidPingResponsePacket:packet] ) { + if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(simplePing:didReceivePingResponsePacket:)] ) { + [self.delegate simplePing:self didReceivePingResponsePacket:packet]; + } } else { - - // We failed to read the data, so shut everything down. - - if (err == 0) { - err = EPIPE; - } - [self _didFailWithError:[NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]]; + if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(simplePing:didReceiveUnexpectedPacket:)] ) { + [self.delegate simplePing:self didReceiveUnexpectedPacket:packet]; + } + } + } else { + + // We failed to read the data, so shut everything down. + + if (err == 0) { + err = EPIPE; } - - free(buffer); - - // Note that we don't loop back trying to read more data. Rather, we just - // let CFSocket call us again. + [self didFailWithError:[NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]]; + } + + free(buffer); + + // Note that we don't loop back trying to read more data. Rather, we just + // let CFSocket call us again. } static void SocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) - // This C routine is called by CFSocket when there's data waiting on our - // ICMP socket. It just redirects the call to Objective-C code. +// This C routine is called by CFSocket when there's data waiting on our +// ICMP socket. It just redirects the call to Objective-C code. { - SimplePing * obj; - - obj = (SimplePing *) info; - assert([obj isKindOfClass:[SimplePing class]]); - - #pragma unused(s) - assert(s == obj->_socket); - #pragma unused(type) - assert(type == kCFSocketReadCallBack); - #pragma unused(address) - assert(address == nil); - #pragma unused(data) - assert(data == nil); - - [obj _readData]; + SimplePing * obj; + + obj = (__bridge SimplePing *) info; + assert([obj isKindOfClass:[SimplePing class]]); + +#pragma unused(s) + assert(s == obj->_socket); +#pragma unused(type) + assert(type == kCFSocketReadCallBack); +#pragma unused(address) + assert(address == nil); +#pragma unused(data) + assert(data == nil); + + [obj readData]; } -- (void)_startWithHostAddress - // We have a host address, so let's actually start pinging it. +- (void)startWithHostAddress +// We have a host address, so let's actually start pinging it. { - int err; - int fd; - const struct sockaddr * addrPtr; + int err; + int fd; + const struct sockaddr * addrPtr; - assert(self.hostAddress != nil); + assert(self.hostAddress != nil); - // Open the socket. - - addrPtr = (const struct sockaddr *) [self.hostAddress bytes]; + // Open the socket. + addrPtr = (const struct sockaddr *) [self.hostAddress bytes]; + + fd = -1; + err = 0; + switch (addrPtr->sa_family) { + case AF_INET: { + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + if (fd < 0) { + err = errno; + } + } break; + case AF_INET6: + assert(NO); + // fall through + default: { + err = EPROTONOSUPPORT; + } break; + } + + if (err != 0) { + [self didFailWithError:[NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]]; + } else { + CFSocketContext context = {0, (__bridge void *)(self), NULL, NULL, NULL}; + CFRunLoopSourceRef rls; + + // Wrap it in a CFSocket and schedule it on the runloop. + + self->_socket = CFSocketCreateWithNative(NULL, fd, kCFSocketReadCallBack, SocketReadCallback, &context); + assert(self->_socket != NULL); + + // The socket will now take care of cleaning up our file descriptor. + + assert( CFSocketGetSocketFlags(self->_socket) & kCFSocketCloseOnInvalidate ); fd = -1; - err = 0; - switch (addrPtr->sa_family) { - case AF_INET: { - fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); - if (fd < 0) { - err = errno; - } - } break; - case AF_INET6: - assert(NO); - // fall through - default: { - err = EPROTONOSUPPORT; - } break; - } - - if (err != 0) { - [self _didFailWithError:[NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]]; - } else { - CFSocketContext context = {0, self, NULL, NULL, NULL}; - CFRunLoopSourceRef rls; - - // Wrap it in a CFSocket and schedule it on the runloop. - - self->_socket = CFSocketCreateWithNative(NULL, fd, kCFSocketReadCallBack, SocketReadCallback, &context); - assert(self->_socket != NULL); - - // The socket will now take care of clean up our file descriptor. - - assert( CFSocketGetSocketFlags(self->_socket) & kCFSocketCloseOnInvalidate ); - fd = -1; - - rls = CFSocketCreateRunLoopSource(NULL, self->_socket, 0); - assert(rls != NULL); - - CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); - - CFRelease(rls); - - if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(simplePing:didStartWithAddress:)] ) { - [self.delegate simplePing:self didStartWithAddress:self.hostAddress]; - } + + rls = CFSocketCreateRunLoopSource(NULL, self->_socket, 0); + assert(rls != NULL); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); + + CFRelease(rls); + + if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(simplePing:didStartWithAddress:)] ) { + [self.delegate simplePing:self didStartWithAddress:self.hostAddress]; } - assert(fd == -1); + } + assert(fd == -1); } -- (void)_hostResolutionDone - // Called by our CFHost resolution callback (HostResolveCallback) when host - // resolution is complete. We just latch the first IPv4 address and kick - // off the pinging process. +- (void)hostResolutionDone +// Called by our CFHost resolution callback (HostResolveCallback) when host +// resolution is complete. We just latch the first IPv4 address and kick +// off the pinging process. { - Boolean resolved; - NSArray * addresses; - - // Find the first IPv4 address. - - addresses = (NSArray *) CFHostGetAddressing(self->_host, &resolved); - if ( resolved && (addresses != nil) ) { - resolved = false; - for (NSData * address in addresses) { - const struct sockaddr * addrPtr; - - addrPtr = (const struct sockaddr *) [address bytes]; - if ( [address length] >= sizeof(struct sockaddr) && addrPtr->sa_family == AF_INET) { - self.hostAddress = address; - resolved = true; - break; - } - } + Boolean resolved; + NSArray * addresses; + + // Find the first IPv4 address. + + addresses = (__bridge NSArray *) CFHostGetAddressing(self->_host, &resolved); + if ( resolved && (addresses != nil) ) { + resolved = false; + for (NSData * address in addresses) { + const struct sockaddr * addrPtr; + + addrPtr = (const struct sockaddr *) [address bytes]; + if ( [address length] >= sizeof(struct sockaddr) && addrPtr->sa_family == AF_INET) { + self.hostAddress = address; + resolved = true; + break; + } } + } - // We're done resolving, so shut that down. - - [self _stopHostResolution]; - - // If all is OK, start pinging, otherwise shut down the pinger completely. - - if (resolved) { - [self _startWithHostAddress]; - } else { - [self _didFailWithError:[NSError errorWithDomain:(NSString *)kCFErrorDomainCFNetwork code:kCFHostErrorHostNotFound userInfo:nil]]; - } + // We're done resolving, so shut that down. + + [self stopHostResolution]; + + // If all is OK, start pinging, otherwise shut down the pinger completely. + + if (resolved) { + [self startWithHostAddress]; + } else { + [self didFailWithError:[NSError errorWithDomain:(NSString *)kCFErrorDomainCFNetwork code:kCFHostErrorHostNotFound userInfo:nil]]; + } } static void HostResolveCallback(CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError *error, void *info) - // This C routine is called by CFHost when the host resolution is complete. - // It just redirects the call to the appropriate Objective-C method. +// This C routine is called by CFHost when the host resolution is complete. +// It just redirects the call to the appropriate Objective-C method. { - SimplePing * obj; - - obj = (SimplePing *) info; - assert([obj isKindOfClass:[SimplePing class]]); - - #pragma unused(theHost) - assert(theHost == obj->_host); - #pragma unused(typeInfo) - assert(typeInfo == kCFHostAddresses); - - if ( (error != NULL) && (error->domain != 0) ) { - [obj _didFailWithHostStreamError:*error]; - } else { - [obj _hostResolutionDone]; - } + SimplePing * obj; + + NSLog(@">HostResolveCallback"); + + obj = (__bridge SimplePing *) info; + assert([obj isKindOfClass:[SimplePing class]]); + +#pragma unused(theHost) + assert(theHost == obj->_host); +#pragma unused(typeInfo) + assert(typeInfo == kCFHostAddresses); + + if ( (error != NULL) && (error->domain != 0) ) { + [obj didFailWithHostStreamError:*error]; + } else { + [obj hostResolutionDone]; + } } - (void)start - // See comment in header. +// See comment in header. { - // If the user supplied us with an address, just start pinging that. Otherwise - // start a host resolution. - - if (self->_hostAddress != nil) { - [self _startWithHostAddress]; - } else { - Boolean success; - CFHostClientContext context = {0, self, NULL, NULL, NULL}; - CFStreamError streamError; - - assert(self->_host == NULL); - - self->_host = CFHostCreateWithName(NULL, (CFStringRef) self.hostName); - assert(self->_host != NULL); - - CFHostSetClient(self->_host, HostResolveCallback, &context); - - CFHostScheduleWithRunLoop(self->_host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); - - success = CFHostStartInfoResolution(self->_host, kCFHostAddresses, &streamError); - if ( ! success ) { - [self _didFailWithHostStreamError:streamError]; - } + // If the user supplied us with an address, just start pinging that. Otherwise + // start a host resolution. + + if (self->_hostAddress != nil) { + [self startWithHostAddress]; + } else { + Boolean success; + CFHostClientContext context = {0, (__bridge void *)(self), NULL, NULL, NULL}; + CFStreamError streamError; + + assert(self->_host == NULL); + + self->_host = CFHostCreateWithName(NULL, (__bridge CFStringRef) self.hostName); + assert(self->_host != NULL); + + CFHostSetClient(self->_host, HostResolveCallback, &context); + + CFHostScheduleWithRunLoop(self->_host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + + NSLog(@">CFHostStartInfoResolution"); + success = CFHostStartInfoResolution(self->_host, kCFHostAddresses, &streamError); + NSLog(@"_host != NULL) { - CFHostSetClient(self->_host, NULL, NULL); - CFHostUnscheduleFromRunLoop(self->_host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); - CFRelease(self->_host); - self->_host = NULL; - } + if (self->_host != NULL) { + CFHostSetClient(self->_host, NULL, NULL); + CFHostUnscheduleFromRunLoop(self->_host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + CFRelease(self->_host); + self->_host = NULL; + } } -- (void)_stopDataTransfer - // Shut down anything to do with sending and receiving pings. +- (void)stopDataTransfer +// Shut down anything to do with sending and receiving pings. { - if (self->_socket != NULL) { - CFSocketInvalidate(self->_socket); - CFRelease(self->_socket); - self->_socket = NULL; - } + if (self->_socket != NULL) { + CFSocketInvalidate(self->_socket); + CFRelease(self->_socket); + self->_socket = NULL; + } } - (void)stop - // See comment in header. +// See comment in header. { - [self _stopHostResolution]; - [self _stopDataTransfer]; - - // If we were started with a host name, junk the host address on stop. If the - // client calls -start again, we'll re-resolve the host name. - - if (self.hostName != nil) { - self.hostAddress = NULL; - } + [self stopHostResolution]; + [self stopDataTransfer]; + + // If we were started with a host name, junk the host address on stop. If the + // client calls -start again, we'll re-resolve the host name. + + if (self.hostName != nil) { + self.hostAddress = NULL; + } } -@end +@end \ No newline at end of file diff --git a/PingTester/SimplePingHelper.h b/PingTester/SimplePingHelper.h index a9133cd..39d403a 100644 --- a/PingTester/SimplePingHelper.h +++ b/PingTester/SimplePingHelper.h @@ -11,6 +11,10 @@ @interface SimplePingHelper : NSObject +@property(nonatomic, copy) void (^callback)(NSNumber *); + + (void)ping:(NSString*)address target:(id)target sel:(SEL)sel; ++ (void)ping:(NSString *)string callback:(void (^)(NSNumber *))callback; + @end diff --git a/PingTester/SimplePingHelper.m b/PingTester/SimplePingHelper.m index 6a66147..8520270 100644 --- a/PingTester/SimplePingHelper.m +++ b/PingTester/SimplePingHelper.m @@ -24,7 +24,11 @@ @implementation SimplePingHelper // Pings the address, and calls the selector when done. Selector must take a NSnumber which is a bool for success + (void)ping:(NSString*)address target:(id)target sel:(SEL)sel { // The helper retains itself through the timeout function - [[[[SimplePingHelper alloc] initWithAddress:address target:target sel:sel] autorelease] go]; + [[[SimplePingHelper alloc] initWithAddress:address target:target sel:sel] go]; +} + ++ (void)ping:(NSString *)string callback:(void (^)(NSNumber *))callback { + [[[SimplePingHelper alloc] initWithAddress:string callback:callback] go]; } #pragma mark - Init/dealloc @@ -32,7 +36,6 @@ + (void)ping:(NSString*)address target:(id)target sel:(SEL)sel { - (void)dealloc { self.simplePing = nil; self.target = nil; - [super dealloc]; } - (id)initWithAddress:(NSString*)address target:(id)_target sel:(SEL)_sel { @@ -45,6 +48,15 @@ - (id)initWithAddress:(NSString*)address target:(id)_target sel:(SEL)_sel { return self; } +- (id)initWithAddress:(NSString*)address callback:(void (^)(NSNumber *))callback { + if (self = [self init]) { + self.simplePing = [SimplePing simplePingWithHostName:address]; + self.simplePing.delegate = self; + self.callback = callback; + } + return self; +} + #pragma mark - Go - (void)go { @@ -57,18 +69,33 @@ - (void)go { // Called on success or failure to clean up - (void)killPing { [self.simplePing stop]; - [[self.simplePing retain] autorelease]; // In case, higher up the call stack, this got called by the simpleping object itself self.simplePing = nil; } - (void)successPing { [self killPing]; - [target performSelector:sel withObject:[NSNumber numberWithBool:YES]]; + + if(self.callback) + { + self.callback([NSNumber numberWithBool:YES]); + } + else + { + [target performSelector:sel withObject:[NSNumber numberWithBool:YES]]; + } } - (void)failPing:(NSString*)reason { [self killPing]; - [target performSelector:sel withObject:[NSNumber numberWithBool:NO]]; + + if(self.callback) + { + self.callback([NSNumber numberWithBool:NO]); + } + else + { + [target performSelector:sel withObject:[NSNumber numberWithBool:NO]]; + } } // Called 1s after ping start, to check if it timed out diff --git a/readme.markdown b/readme.markdown index d7e85a4..4864752 100644 --- a/readme.markdown +++ b/readme.markdown @@ -1,8 +1,24 @@ Simple Ping Helper ================== -How to perform a Ping in an iPhone app +(Fork自:[SimplePingHelper](https://github.com/chrishulbert/SimplePingHelper)) -For more information, how to use, see: +###更新了SimplePing 的源码:[SimplePing](https://developer.apple.com/library/mac/samplecode/SimplePing/Listings/SimplePing_m.html#//apple_ref/doc/uid/DTS10000716-SimplePing_m-DontLinkElementID_5) -http://splinter.com.au/how-to-ping-a-server-in-objective-c-iphone +###增加了ARC + +###增加了通过回调来处理事件: + [SimplePingHelper ping:@"www.baidu.com" + callback:^(NSNumber *b){ + if(b.boolValue) + { + NSLog(@"%@", @"success"); + [self log:@"success"]; + + } + else + { + NSLog(@"%@", @"failure"); + [self log:@"failure"]; + } + }]; \ No newline at end of file