4 // This class is in the public domain.
5 // Originally created by Dustin Voss on Wed Jan 29 2003.
6 // Updated and maintained by Deusty Designs and the Mac development community.
8 // http://code.google.com/p/cocoaasyncsocket/
11 #import "AsyncSocket.h"
12 #import <sys/socket.h>
13 #import <netinet/in.h>
18 // Note: You may need to add the CFNetwork Framework to your project
19 #import <CFNetwork/CFNetwork.h>
22 #pragma mark Declarations
24 #define DEFAULT_PREBUFFERING YES // Whether pre-buffering is enabled by default
26 #define READQUEUE_CAPACITY 5 // Initial capacity
27 #define WRITEQUEUE_CAPACITY 5 // Initial capacity
28 #define READALL_CHUNKSIZE 256 // Incremental increase in buffer size
29 #define WRITE_CHUNKSIZE (1024 * 4) // Limit on size of each write pass
31 NSString *const AsyncSocketException = @"AsyncSocketException";
32 NSString *const AsyncSocketErrorDomain = @"AsyncSocketErrorDomain";
34 // Mutex lock used by all instances of AsyncSocket, to protect getaddrinfo.
35 // The man page says it is not thread-safe. (As of Mac OS X 10.4.7, and possibly earlier)
36 static NSString *getaddrinfoLock = @"lock";
40 kEnablePreBuffering = 1 << 0, // If set, pre-buffering is enabled
41 kDidPassConnectMethod = 1 << 1, // If set, disconnection results in delegate call
42 kDidCallConnectDelegate = 1 << 2, // If set, connect delegate has been called
43 kStartingTLS = 1 << 3, // If set, we're waiting for TLS negotiation to complete
44 kForbidReadsWrites = 1 << 4, // If set, no new reads or writes are allowed
45 kDisconnectAfterReads = 1 << 5, // If set, disconnect after no more reads are queued
46 kDisconnectAfterWrites = 1 << 6, // If set, disconnect after no more writes are queued
47 kClosingWithError = 1 << 7, // If set, the socket is being closed due to an error
50 @interface AsyncSocket (Private)
52 // Socket Implementation
53 - (CFSocketRef) createAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr;
54 - (BOOL) createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
55 - (BOOL) attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr;
56 - (BOOL) configureSocketAndReturnError:(NSError **)errPtr;
57 - (BOOL) connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
58 - (void) doAcceptWithSocket:(CFSocketNativeHandle)newSocket;
59 - (void) doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)err;
61 // Stream Implementation
62 - (BOOL) createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr;
63 - (BOOL) createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr;
64 - (BOOL) attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr;
65 - (BOOL) configureStreamsAndReturnError:(NSError **)errPtr;
66 - (BOOL) openStreamsAndReturnError:(NSError **)errPtr;
67 - (void) doStreamOpen;
68 - (BOOL) setSocketFromStreamsAndReturnError:(NSError **)errPtr;
70 // Disconnect Implementation
71 - (void) closeWithError:(NSError *)err;
72 - (void) recoverUnreadData;
77 - (NSError *) getErrnoError;
78 - (NSError *) getAbortError;
79 - (NSError *) getStreamError;
80 - (NSError *) getSocketError;
81 - (NSError *) getReadMaxedOutError;
82 - (NSError *) getReadTimeoutError;
83 - (NSError *) getWriteTimeoutError;
84 - (NSError *) errorFromCFStreamError:(CFStreamError)err;
87 - (BOOL) isSocketConnected;
88 - (BOOL) areStreamsConnected;
89 - (NSString *) connectedHost: (CFSocketRef)socket;
90 - (UInt16) connectedPort: (CFSocketRef)socket;
91 - (NSString *) localHost: (CFSocketRef)socket;
92 - (UInt16) localPort: (CFSocketRef)socket;
93 - (NSString *) addressHost: (CFDataRef)cfaddr;
94 - (UInt16) addressPort: (CFDataRef)cfaddr;
97 - (void) doBytesAvailable;
98 - (void) completeCurrentRead;
99 - (void) endCurrentRead;
100 - (void) scheduleDequeueRead;
101 - (void) maybeDequeueRead;
102 - (void) doReadTimeout:(NSTimer *)timer;
105 - (void) doSendBytes;
106 - (void) completeCurrentWrite;
107 - (void) endCurrentWrite;
108 - (void) scheduleDequeueWrite;
109 - (void) maybeDequeueWrite;
110 - (void) maybeScheduleDisconnect;
111 - (void) doWriteTimeout:(NSTimer *)timer;
114 - (void)maybeStartTLS;
115 - (void)onTLSStarted:(BOOL)flag;
118 - (void) doCFCallback:(CFSocketCallBackType)type forSocket:(CFSocketRef)sock withAddress:(NSData *)address withData:(const void *)pData;
119 - (void) doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream;
120 - (void) doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream;
124 static void MyCFSocketCallback (CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *);
125 static void MyCFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo);
126 static void MyCFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType type, void *pInfo);
128 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
130 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
133 * The AsyncReadPacket encompasses the instructions for any given read.
134 * The content of a read packet allows the code to determine if we're:
135 * - reading to a certain length
136 * - reading to a certain separator
137 * - or simply reading the first chunk of available data
139 @interface AsyncReadPacket : NSObject
142 NSMutableData *buffer;
144 NSTimeInterval timeout;
148 BOOL readAllAvailableData;
150 - (id)initWithData:(NSMutableData *)d
151 timeout:(NSTimeInterval)t
153 readAllAvailable:(BOOL)a
154 terminator:(NSData *)e
155 maxLength:(CFIndex)m;
157 - (unsigned)readLengthForTerm;
159 - (unsigned)prebufferReadLengthForTerm;
160 - (CFIndex)searchForTermAfterPreBuffering:(CFIndex)numBytes;
163 @implementation AsyncReadPacket
165 - (id)initWithData:(NSMutableData *)d
166 timeout:(NSTimeInterval)t
168 readAllAvailable:(BOOL)a
169 terminator:(NSData *)e
172 if(self = [super init])
177 readAllAvailableData = a;
186 * For read packets with a set terminator, returns the safe length of data that can be read
187 * without going over a terminator, or the maxLength.
189 * It is assumed the terminator has not already been read.
191 - (unsigned)readLengthForTerm
193 NSAssert(term != nil, @"Searching for term in data when there is no term.");
195 // What we're going to do is look for a partial sequence of the terminator at the end of the buffer.
196 // If a partial sequence occurs, then we must assume the next bytes to arrive will be the rest of the term,
197 // and we can only read that amount.
198 // Otherwise, we're safe to read the entire length of the term.
200 unsigned result = [term length];
202 // Shortcut when term is a single byte
203 if(result == 1) return result;
205 // i = index within buffer at which to check data
206 // j = length of term to check against
208 // Note: Beware of implicit casting rules
209 // This could give you -1: MAX(0, (0 - [term length] + 1));
211 CFIndex i = MAX(0, (CFIndex)(bytesDone - [term length] + 1));
212 CFIndex j = MIN([term length] - 1, bytesDone);
216 const void *subBuffer = [buffer bytes] + i;
218 if(memcmp(subBuffer, [term bytes], j) == 0)
220 result = [term length] - j;
229 return MIN(result, (maxLength - bytesDone));
235 * Assuming pre-buffering is enabled, returns the amount of data that can be read
236 * without going over the maxLength.
238 - (unsigned)prebufferReadLengthForTerm
241 return MIN(READALL_CHUNKSIZE, (maxLength - bytesDone));
243 return READALL_CHUNKSIZE;
247 * For read packets with a set terminator, scans the packet buffer for the term.
248 * It is assumed the terminator had not been fully read prior to the new bytes.
250 * If the term is found, the number of excess bytes after the term are returned.
251 * If the term is not found, this method will return -1.
253 * Note: A return value of zero means the term was found at the very end.
255 - (CFIndex)searchForTermAfterPreBuffering:(CFIndex)numBytes
257 NSAssert(term != nil, @"Searching for term in data when there is no term.");
259 // We try to start the search such that the first new byte read matches up with the last byte of the term.
260 // We continue searching forward after this until the term no longer fits into the buffer.
262 // Note: Beware of implicit casting rules
263 // This could give you -1: MAX(0, 1 - 1 - [term length] + 1);
265 CFIndex i = MAX(0, (CFIndex)(bytesDone - numBytes - [term length] + 1));
267 while(i + [term length] <= bytesDone)
269 const void *subBuffer = [buffer bytes] + i;
271 if(memcmp(subBuffer, [term bytes], [term length]) == 0)
273 return bytesDone - (i + [term length]);
291 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
293 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
296 * The AsyncWritePacket encompasses the instructions for any given write.
298 @interface AsyncWritePacket : NSObject
304 NSTimeInterval timeout;
306 - (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i;
309 @implementation AsyncWritePacket
311 - (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i
313 if(self = [super init])
331 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
333 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
336 * The AsyncSpecialPacket encompasses special instructions for interruptions in the read/write queues.
337 * This class my be altered to support more than just TLS in the future.
339 @interface AsyncSpecialPacket : NSObject
342 NSDictionary *tlsSettings;
344 - (id)initWithTLSSettings:(NSDictionary *)settings;
347 @implementation AsyncSpecialPacket
349 - (id)initWithTLSSettings:(NSDictionary *)settings
351 if(self = [super init])
353 tlsSettings = [settings copy];
360 [tlsSettings release];
366 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
368 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
370 @implementation AsyncSocket
374 return [self initWithDelegate:nil userData:0];
377 - (id)initWithDelegate:(id)delegate
379 return [self initWithDelegate:delegate userData:0];
382 // Designated initializer.
383 - (id)initWithDelegate:(id)delegate userData:(long)userData
385 if(self = [super init])
387 theFlags = DEFAULT_PREBUFFERING ? kEnablePreBuffering : 0x00;
388 theDelegate = delegate;
389 theUserData = userData;
396 theReadStream = NULL;
397 theWriteStream = NULL;
399 theReadQueue = [[NSMutableArray alloc] initWithCapacity:READQUEUE_CAPACITY];
400 theCurrentRead = nil;
403 partialReadBuffer = [[NSMutableData alloc] initWithCapacity:READALL_CHUNKSIZE];
405 theWriteQueue = [[NSMutableArray alloc] initWithCapacity:WRITEQUEUE_CAPACITY];
406 theCurrentWrite = nil;
410 NSAssert(sizeof(CFSocketContext) == sizeof(CFStreamClientContext), @"CFSocketContext != CFStreamClientContext");
411 theContext.version = 0;
412 theContext.info = self;
413 theContext.retain = nil;
414 theContext.release = nil;
415 theContext.copyDescription = nil;
417 // Default run loop modes
418 theRunLoopModes = [[NSArray arrayWithObject:NSDefaultRunLoopMode] retain];
423 // The socket may been initialized in a connected state and auto-released, so this should close it down cleanly.
427 [theReadQueue release];
428 [theWriteQueue release];
429 [theRunLoopModes release];
430 [NSObject cancelPreviousPerformRequestsWithTarget:theDelegate selector:@selector(onSocketDidDisconnect:) object:self];
431 [NSObject cancelPreviousPerformRequestsWithTarget:self];
435 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
436 #pragma mark Accessors
437 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
444 - (void)setUserData:(long)userData
446 theUserData = userData;
454 - (void)setDelegate:(id)delegate
456 theDelegate = delegate;
459 - (BOOL)canSafelySetDelegate
461 return ([theReadQueue count] == 0 && [theWriteQueue count] == 0 && theCurrentRead == nil && theCurrentWrite == nil);
464 - (CFSocketRef)getCFSocket
472 - (CFReadStreamRef)getCFReadStream
474 return theReadStream;
477 - (CFWriteStreamRef)getCFWriteStream
479 return theWriteStream;
482 - (float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total
484 // Check to make sure we're actually reading something right now
485 if (!theCurrentRead) return NAN;
487 // It's only possible to know the progress of our read if we're reading to a certain length
488 // If we're reading to data, we of course have no idea when the data will arrive
489 // If we're reading to timeout, then we have no idea when the next chunk of data will arrive.
490 BOOL hasTotal = (theCurrentRead->readAllAvailableData == NO && theCurrentRead->term == nil);
492 CFIndex d = theCurrentRead->bytesDone;
493 CFIndex t = hasTotal ? [theCurrentRead->buffer length] : 0;
494 if (tag != NULL) *tag = theCurrentRead->tag;
495 if (done != NULL) *done = d;
496 if (total != NULL) *total = t;
497 float ratio = (float)d/(float)t;
498 return isnan(ratio) ? 1.0 : ratio; // 0 of 0 bytes is 100% done.
501 - (float)progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total
503 if (!theCurrentWrite) return NAN;
504 CFIndex d = theCurrentWrite->bytesDone;
505 CFIndex t = [theCurrentWrite->buffer length];
506 if (tag != NULL) *tag = theCurrentWrite->tag;
507 if (done != NULL) *done = d;
508 if (total != NULL) *total = t;
509 return (float)d/(float)t;
512 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
513 #pragma mark Configuration
514 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
517 * See the header file for a full explanation of pre-buffering.
519 - (void)enablePreBuffering
521 theFlags |= kEnablePreBuffering;
525 * See the header file for a full explanation of this method.
527 - (BOOL)moveToRunLoop:(NSRunLoop *)runLoop
529 NSAssert((theRunLoop == CFRunLoopGetCurrent()), @"moveToRunLoop must be called from within the current RunLoop!");
535 if(theRunLoop == [runLoop getCFRunLoop])
542 [NSObject cancelPreviousPerformRequestsWithTarget:self];
544 if(theReadStream && theWriteStream)
546 for(i = 0; i < [theRunLoopModes count]; i++)
548 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
550 CFReadStreamUnscheduleFromRunLoop(theReadStream, theRunLoop, runLoopMode);
551 CFWriteStreamUnscheduleFromRunLoop(theWriteStream, theRunLoop, runLoopMode);
553 CFReadStreamSetClient(theReadStream, kCFStreamEventNone, NULL, NULL);
554 CFWriteStreamSetClient(theWriteStream, kCFStreamEventNone, NULL, NULL);
558 for(i = 0; i < [theRunLoopModes count]; i++)
560 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
561 CFRunLoopRemoveSource(theRunLoop, theSource, runLoopMode);
566 for(i = 0; i < [theRunLoopModes count]; i++)
568 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
569 CFRunLoopRemoveSource(theRunLoop, theSource6, runLoopMode);
574 // We do not retain the read timer - it gets retained by the runloop when we add it as a source.
575 // Since we're about to remove it as a source, we retain it now, and release it again below.
576 [theReadTimer retain];
578 for(i = 0; i < [theRunLoopModes count]; i++)
580 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
581 CFRunLoopRemoveTimer(theRunLoop, (CFRunLoopTimerRef)theReadTimer, runLoopMode);
586 // We do not retain the write timer - it gets retained by the runloop when we add it as a source.
587 // Since we're about to remove it as a source, we retain it now, and release it again below.
588 [theWriteTimer retain];
590 for(i = 0; i < [theRunLoopModes count]; i++)
592 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
593 CFRunLoopRemoveTimer(theRunLoop, (CFRunLoopTimerRef)theWriteTimer, runLoopMode);
597 theRunLoop = [runLoop getCFRunLoop];
601 for(i = 0; i < [theRunLoopModes count]; i++)
603 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
604 CFRunLoopAddSource(theRunLoop, theSource, runLoopMode);
609 for(i = 0; i < [theRunLoopModes count]; i++)
611 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
612 CFRunLoopAddSource(theRunLoop, theSource6, runLoopMode);
615 if(theReadStream && theWriteStream)
617 if(![self attachStreamsToRunLoop:runLoop error:nil])
624 for(i = 0; i < [theRunLoopModes count]; i++)
626 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
627 CFRunLoopAddTimer(theRunLoop, (CFRunLoopTimerRef)theReadTimer, runLoopMode);
630 // Release here since we retained it above
631 [theReadTimer release];
635 for(i = 0; i < [theRunLoopModes count]; i++)
637 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
638 CFRunLoopAddTimer(theRunLoop, (CFRunLoopTimerRef)theWriteTimer, runLoopMode);
641 // Release here since we retained it above
642 [theWriteTimer release];
645 [runLoop performSelector:@selector(maybeDequeueRead) target:self argument:nil order:0 modes:theRunLoopModes];
646 [runLoop performSelector:@selector(maybeDequeueWrite) target:self argument:nil order:0 modes:theRunLoopModes];
647 [runLoop performSelector:@selector(maybeScheduleDisconnect) target:self argument:nil order:0 modes:theRunLoopModes];
653 * See the header file for a full explanation of this method.
655 - (BOOL)setRunLoopModes:(NSArray *)runLoopModes
657 if([runLoopModes count] == 0)
661 if([theRunLoopModes isEqualToArray:runLoopModes])
668 [NSObject cancelPreviousPerformRequestsWithTarget:self];
670 if(theReadStream && theWriteStream)
672 for(i = 0; i < [theRunLoopModes count]; i++)
674 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
676 CFReadStreamUnscheduleFromRunLoop(theReadStream, theRunLoop, runLoopMode);
677 CFWriteStreamUnscheduleFromRunLoop(theWriteStream, theRunLoop, runLoopMode);
679 CFReadStreamSetClient(theReadStream, kCFStreamEventNone, NULL, NULL);
680 CFWriteStreamSetClient(theWriteStream, kCFStreamEventNone, NULL, NULL);
684 for(i = 0; i < [theRunLoopModes count]; i++)
686 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
687 CFRunLoopRemoveSource(theRunLoop, theSource, runLoopMode);
692 for(i = 0; i < [theRunLoopModes count]; i++)
694 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
695 CFRunLoopRemoveSource(theRunLoop, theSource6, runLoopMode);
700 // We do not retain the read timer - it gets retained by the runloop when we add it as a source.
701 // Since we're about to remove it as a source, we retain it now, and release it again below.
702 [theReadTimer retain];
704 for(i = 0; i < [theRunLoopModes count]; i++)
706 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
707 CFRunLoopRemoveTimer(theRunLoop, (CFRunLoopTimerRef)theReadTimer, runLoopMode);
712 // We do not retain the write timer - it gets retained by the runloop when we add it as a source.
713 // Since we're about to remove it as a source, we retain it now, and release it again below.
714 [theWriteTimer retain];
716 for(i = 0; i < [theRunLoopModes count]; i++)
718 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
719 CFRunLoopRemoveTimer(theRunLoop, (CFRunLoopTimerRef)theWriteTimer, runLoopMode);
723 [theRunLoopModes release];
724 theRunLoopModes = [runLoopModes copy];
728 for(i = 0; i < [theRunLoopModes count]; i++)
730 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
731 CFRunLoopAddSource(theRunLoop, theSource, runLoopMode);
736 for(i = 0; i < [theRunLoopModes count]; i++)
738 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
739 CFRunLoopAddSource(theRunLoop, theSource6, runLoopMode);
742 if(theReadStream && theWriteStream)
744 if(![self attachStreamsToRunLoop:(NSRunLoop *)theRunLoop error:nil])
751 for(i = 0; i < [theRunLoopModes count]; i++)
753 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
754 CFRunLoopAddTimer(theRunLoop, (CFRunLoopTimerRef)theReadTimer, runLoopMode);
757 // Release here since we retained it above
758 [theReadTimer release];
762 for(i = 0; i < [theRunLoopModes count]; i++)
764 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
765 CFRunLoopAddTimer(theRunLoop, (CFRunLoopTimerRef)theWriteTimer, runLoopMode);
768 // Release here since we retained it above
769 [theWriteTimer release];
772 [self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes];
773 [self performSelector:@selector(maybeDequeueWrite) withObject:nil afterDelay:0 inModes:theRunLoopModes];
774 [self performSelector:@selector(maybeScheduleDisconnect) withObject:nil afterDelay:0 inModes:theRunLoopModes];
779 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
780 #pragma mark Connection
781 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
783 - (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr
785 return [self acceptOnAddress:nil port:port error:errPtr];
789 * To accept on a certain address, pass the address to accept on.
790 * To accept on any address, pass nil or an empty string.
791 * To accept only connections from localhost pass "localhost" or "loopback".
793 - (BOOL)acceptOnAddress:(NSString *)hostaddr port:(UInt16)port error:(NSError **)errPtr
795 if (theDelegate == NULL)
796 [NSException raise:AsyncSocketException format:@"Attempting to accept without a delegate. Set a delegate first."];
798 if (theSocket != NULL || theSocket6 != NULL)
799 [NSException raise:AsyncSocketException format:@"Attempting to accept while connected or accepting connections. Disconnect first."];
801 // Set up the listen sockaddr structs if needed.
803 NSData *address = nil, *address6 = nil;
804 if(hostaddr == nil || ([hostaddr length] == 0))
806 // Accept on ANY address
807 struct sockaddr_in nativeAddr;
808 nativeAddr.sin_len = sizeof(struct sockaddr_in);
809 nativeAddr.sin_family = AF_INET;
810 nativeAddr.sin_port = htons(port);
811 nativeAddr.sin_addr.s_addr = htonl(INADDR_ANY);
812 memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero));
814 struct sockaddr_in6 nativeAddr6;
815 nativeAddr6.sin6_len = sizeof(struct sockaddr_in6);
816 nativeAddr6.sin6_family = AF_INET6;
817 nativeAddr6.sin6_port = htons(port);
818 nativeAddr6.sin6_flowinfo = 0;
819 nativeAddr6.sin6_addr = in6addr_any;
820 nativeAddr6.sin6_scope_id = 0;
822 // Wrap the native address structures for CFSocketSetAddress.
823 address = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)];
824 address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
826 else if([hostaddr isEqualToString:@"localhost"] || [hostaddr isEqualToString:@"loopback"])
828 // Accept only on LOOPBACK address
829 struct sockaddr_in nativeAddr;
830 nativeAddr.sin_len = sizeof(struct sockaddr_in);
831 nativeAddr.sin_family = AF_INET;
832 nativeAddr.sin_port = htons(port);
833 nativeAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
834 memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero));
836 struct sockaddr_in6 nativeAddr6;
837 nativeAddr6.sin6_len = sizeof(struct sockaddr_in6);
838 nativeAddr6.sin6_family = AF_INET6;
839 nativeAddr6.sin6_port = htons(port);
840 nativeAddr6.sin6_flowinfo = 0;
841 nativeAddr6.sin6_addr = in6addr_loopback;
842 nativeAddr6.sin6_scope_id = 0;
844 // Wrap the native address structures for CFSocketSetAddress.
845 address = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)];
846 address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
850 NSString *portStr = [NSString stringWithFormat:@"%hu", port];
852 @synchronized (getaddrinfoLock)
854 struct addrinfo hints, *res, *res0;
856 memset(&hints, 0, sizeof(hints));
857 hints.ai_family = PF_UNSPEC;
858 hints.ai_socktype = SOCK_STREAM;
859 hints.ai_protocol = IPPROTO_TCP;
860 hints.ai_flags = AI_PASSIVE;
862 int error = getaddrinfo([hostaddr UTF8String], [portStr UTF8String], &hints, &res0);
868 NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding];
869 NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
871 *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info];
875 for(res = res0; res; res = res->ai_next)
877 if(!address && (res->ai_family == AF_INET))
879 // Found IPv4 address
880 // Wrap the native address structures for CFSocketSetAddress.
881 address = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
883 else if(!address6 && (res->ai_family == AF_INET6))
885 // Found IPv6 address
886 // Wrap the native address structures for CFSocketSetAddress.
887 address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
893 if(!address && !address6) return NO;
896 // Create the sockets.
900 theSocket = [self createAcceptSocketForAddress:address error:errPtr];
901 if (theSocket == NULL) goto Failed;
906 theSocket6 = [self createAcceptSocketForAddress:address6 error:errPtr];
908 // Note: The iPhone doesn't currently support IPv6
910 #if !TARGET_OS_IPHONE
911 if (theSocket6 == NULL) goto Failed;
915 // Attach the sockets to the run loop so that callback methods work
917 [self attachSocketsToRunLoop:nil error:nil];
919 // Set the SO_REUSEADDR flags.
922 if (theSocket) setsockopt(CFSocketGetNative(theSocket), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
923 if (theSocket6) setsockopt(CFSocketGetNative(theSocket6), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
925 // Set the local bindings which causes the sockets to start listening.
930 err = CFSocketSetAddress (theSocket, (CFDataRef)address);
931 if (err != kCFSocketSuccess) goto Failed;
933 //NSLog(@"theSocket4: %hu", [self localPort:theSocket]);
936 if(port == 0 && theSocket && theSocket6)
938 // The user has passed in port 0, which means he wants to allow the kernel to choose the port for them
939 // However, the kernel will choose a different port for both theSocket and theSocket6
940 // So we grab the port the kernel choose for theSocket, and set it as the port for theSocket6
941 UInt16 chosenPort = [self localPort:theSocket];
943 struct sockaddr_in6 *pSockAddr6 = (struct sockaddr_in6 *)[address6 bytes];
944 pSockAddr6->sin6_port = htons(chosenPort);
949 err = CFSocketSetAddress (theSocket6, (CFDataRef)address6);
950 if (err != kCFSocketSuccess) goto Failed;
952 //NSLog(@"theSocket6: %hu", [self localPort:theSocket6]);
955 theFlags |= kDidPassConnectMethod;
959 if(errPtr) *errPtr = [self getSocketError];
960 if(theSocket != NULL)
962 CFSocketInvalidate(theSocket);
963 CFRelease(theSocket);
966 if(theSocket6 != NULL)
968 CFSocketInvalidate(theSocket6);
969 CFRelease(theSocket6);
976 * This method creates an initial CFReadStream and CFWriteStream to the given host on the given port.
977 * The connection is then opened, and the corresponding CFSocket will be extracted after the connection succeeds.
979 * Thus the delegate will have access to the CFReadStream and CFWriteStream prior to connection,
980 * specifically in the onSocketWillConnect: method.
982 - (BOOL)connectToHost:(NSString*)hostname onPort:(UInt16)port error:(NSError **)errPtr
984 if(theDelegate == NULL)
986 NSString *message = @"Attempting to connect without a delegate. Set a delegate first.";
987 [NSException raise:AsyncSocketException format:message];
990 if(theSocket != NULL || theSocket6 != NULL)
992 NSString *message = @"Attempting to connect while connected or accepting connections. Disconnect first.";
993 [NSException raise:AsyncSocketException format:message];
998 if(pass && ![self createStreamsToHost:hostname onPort:port error:errPtr]) pass = NO;
999 if(pass && ![self attachStreamsToRunLoop:nil error:errPtr]) pass = NO;
1000 if(pass && ![self configureStreamsAndReturnError:errPtr]) pass = NO;
1001 if(pass && ![self openStreamsAndReturnError:errPtr]) pass = NO;
1004 theFlags |= kDidPassConnectMethod;
1012 * This method creates an initial CFSocket to the given address.
1013 * The connection is then opened, and the corresponding CFReadStream and CFWriteStream will be
1014 * created from the low-level sockets after the connection succeeds.
1016 * Thus the delegate will have access to the CFSocket and CFSocketNativeHandle (BSD socket) prior to connection,
1017 * specifically in the onSocketWillConnect: method.
1019 * Note: The NSData parameter is expected to be a sockaddr structure. For example, an NSData object returned from
1020 * NSNetservice addresses method.
1021 * If you have an existing struct sockaddr you can convert it to an NSData object like so:
1022 * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
1023 * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
1025 - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr
1027 if (theDelegate == NULL)
1029 NSString *message = @"Attempting to connect without a delegate. Set a delegate first.";
1030 [NSException raise:AsyncSocketException format:message];
1033 if (theSocket != NULL || theSocket6 != NULL)
1035 NSString *message = @"Attempting to connect while connected or accepting connections. Disconnect first.";
1036 [NSException raise:AsyncSocketException format:message];
1041 if(pass && ![self createSocketForAddress:remoteAddr error:errPtr]) pass = NO;
1042 if(pass && ![self attachSocketsToRunLoop:nil error:errPtr]) pass = NO;
1043 if(pass && ![self configureSocketAndReturnError:errPtr]) pass = NO;
1044 if(pass && ![self connectSocketToAddress:remoteAddr error:errPtr]) pass = NO;
1047 theFlags |= kDidPassConnectMethod;
1054 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1055 #pragma mark Socket Implementation
1056 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1059 * Creates the accept sockets.
1060 * Returns true if either IPv4 or IPv6 is created.
1061 * If either is missing, an error is returned (even though the method may return true).
1063 - (CFSocketRef)createAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr
1065 struct sockaddr *pSockAddr = (struct sockaddr *)[addr bytes];
1066 int addressFamily = pSockAddr->sa_family;
1068 CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault,
1072 kCFSocketAcceptCallBack, // Callback flags
1073 (CFSocketCallBack)&MyCFSocketCallback, // Callback method
1078 if(errPtr) *errPtr = [self getSocketError];
1084 - (BOOL)createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr
1086 struct sockaddr *pSockAddr = (struct sockaddr *)[remoteAddr bytes];
1088 if(pSockAddr->sa_family == AF_INET)
1090 theSocket = CFSocketCreate(NULL, // Default allocator
1091 PF_INET, // Protocol Family
1092 SOCK_STREAM, // Socket Type
1093 IPPROTO_TCP, // Protocol
1094 kCFSocketConnectCallBack, // Callback flags
1095 (CFSocketCallBack)&MyCFSocketCallback, // Callback method
1096 &theContext); // Socket Context
1098 if(theSocket == NULL)
1100 if (errPtr) *errPtr = [self getSocketError];
1104 else if(pSockAddr->sa_family == AF_INET6)
1106 theSocket6 = CFSocketCreate(NULL, // Default allocator
1107 PF_INET6, // Protocol Family
1108 SOCK_STREAM, // Socket Type
1109 IPPROTO_TCP, // Protocol
1110 kCFSocketConnectCallBack, // Callback flags
1111 (CFSocketCallBack)&MyCFSocketCallback, // Callback method
1112 &theContext); // Socket Context
1114 if(theSocket6 == NULL)
1116 if (errPtr) *errPtr = [self getSocketError];
1122 if (errPtr) *errPtr = [self getSocketError];
1130 * Adds the CFSocket's to the run-loop so that callbacks will work properly.
1132 - (BOOL)attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr
1136 // Get the CFRunLoop to which the socket should be attached.
1137 theRunLoop = (runLoop == nil) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop];
1141 theSource = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket, 0);
1142 for(i = 0; i < [theRunLoopModes count]; i++)
1144 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
1145 CFRunLoopAddSource (theRunLoop, theSource, runLoopMode);
1151 theSource6 = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket6, 0);
1152 for(i = 0; i < [theRunLoopModes count]; i++)
1154 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
1155 CFRunLoopAddSource (theRunLoop, theSource6, runLoopMode);
1163 * Allows the delegate method to configure the CFSocket or CFNativeSocket as desired before we connect.
1164 * Note that the CFReadStream and CFWriteStream will not be available until after the connection is opened.
1166 - (BOOL)configureSocketAndReturnError:(NSError **)errPtr
1168 // Call the delegate method for further configuration.
1169 if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)])
1171 if([theDelegate onSocketWillConnect:self] == NO)
1173 if (errPtr) *errPtr = [self getAbortError];
1180 - (BOOL)connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr
1182 // Start connecting to the given address in the background
1183 // The MyCFSocketCallback method will be called when the connection succeeds or fails
1186 CFSocketError err = CFSocketConnectToAddress(theSocket, (CFDataRef)remoteAddr, -1);
1187 if(err != kCFSocketSuccess)
1189 if (errPtr) *errPtr = [self getSocketError];
1195 CFSocketError err = CFSocketConnectToAddress(theSocket6, (CFDataRef)remoteAddr, -1);
1196 if(err != kCFSocketSuccess)
1198 if (errPtr) *errPtr = [self getSocketError];
1207 * Attempt to make the new socket.
1208 * If an error occurs, ignore this event.
1210 - (void)doAcceptWithSocket:(CFSocketNativeHandle)newNative
1212 // New socket inherits same delegate and run loop modes.
1213 // Note: We use [self class] to support subclassing AsyncSocket.
1214 AsyncSocket *newSocket = [[[[self class] alloc] initWithDelegate:theDelegate] autorelease];
1215 [newSocket setRunLoopModes:theRunLoopModes];
1219 if ([theDelegate respondsToSelector:@selector(onSocket:didAcceptNewSocket:)])
1220 [theDelegate onSocket:self didAcceptNewSocket:newSocket];
1222 NSRunLoop *runLoop = nil;
1223 if ([theDelegate respondsToSelector:@selector(onSocket:wantsRunLoopForNewSocket:)])
1224 runLoop = [theDelegate onSocket:self wantsRunLoopForNewSocket:newSocket];
1228 if(pass && ![newSocket createStreamsFromNative:newNative error:nil]) pass = NO;
1229 if(pass && ![newSocket attachStreamsToRunLoop:runLoop error:nil]) pass = NO;
1230 if(pass && ![newSocket configureStreamsAndReturnError:nil]) pass = NO;
1231 if(pass && ![newSocket openStreamsAndReturnError:nil]) pass = NO;
1234 newSocket->theFlags |= kDidPassConnectMethod;
1236 // No NSError, but errors will still get logged from the above functions.
1244 * Description forthcoming...
1246 - (void)doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)socketError
1248 NSParameterAssert ((sock == theSocket) || (sock == theSocket6));
1250 if(socketError == kCFSocketTimeout || socketError == kCFSocketError)
1252 [self closeWithError:[self getSocketError]];
1256 // Get the underlying native (BSD) socket
1257 CFSocketNativeHandle nativeSocket = CFSocketGetNative(sock);
1259 // Setup the socket so that invalidating the socket will not close the native socket
1260 CFSocketSetSocketFlags(sock, 0);
1262 // Invalidate and release the CFSocket - All we need from here on out is the nativeSocket
1263 // Note: If we don't invalidate the socket (leaving the native socket open)
1264 // then theReadStream and theWriteStream won't function properly.
1265 // Specifically, their callbacks won't work, with the exception of kCFStreamEventOpenCompleted.
1266 // I'm not entirely sure why this is, but I'm guessing that events on the socket fire to the CFSocket we created,
1267 // as opposed to the CFReadStream/CFWriteStream.
1269 CFSocketInvalidate(sock);
1277 if(pass && ![self createStreamsFromNative:nativeSocket error:&err]) pass = NO;
1278 if(pass && ![self attachStreamsToRunLoop:nil error:&err]) pass = NO;
1279 if(pass && ![self openStreamsAndReturnError:&err]) pass = NO;
1283 [self closeWithError:err];
1287 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1288 #pragma mark Stream Implementation
1289 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1292 * Creates the CFReadStream and CFWriteStream from the given native socket.
1293 * The CFSocket may be extracted from either stream after the streams have been opened.
1295 * Note: The given native socket must already be connected!
1297 - (BOOL)createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr
1299 // Create the socket & streams.
1300 CFStreamCreatePairWithSocket(kCFAllocatorDefault, native, &theReadStream, &theWriteStream);
1301 if (theReadStream == NULL || theWriteStream == NULL)
1303 NSError *err = [self getStreamError];
1304 NSLog (@"AsyncSocket %p couldn't create streams from accepted socket: %@", self, err);
1305 if (errPtr) *errPtr = err;
1309 // Ensure the CF & BSD socket is closed when the streams are closed.
1310 CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
1311 CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
1317 * Creates the CFReadStream and CFWriteStream from the given hostname and port number.
1318 * The CFSocket may be extracted from either stream after the streams have been opened.
1320 - (BOOL)createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr
1322 // Create the socket & streams.
1323 CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)hostname, port, &theReadStream, &theWriteStream);
1324 if (theReadStream == NULL || theWriteStream == NULL)
1326 if (errPtr) *errPtr = [self getStreamError];
1330 // Ensure the CF & BSD socket is closed when the streams are closed.
1331 CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
1332 CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
1337 - (BOOL)attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr
1341 // Get the CFRunLoop to which the socket should be attached.
1342 theRunLoop = (runLoop == nil) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop];
1344 // Make read stream non-blocking.
1345 if (!CFReadStreamSetClient (theReadStream,
1346 kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered | kCFStreamEventOpenCompleted,
1347 (CFReadStreamClientCallBack)&MyCFReadStreamCallback,
1348 (CFStreamClientContext *)(&theContext) ))
1350 NSError *err = [self getStreamError];
1352 NSLog (@"AsyncSocket %p couldn't attach read stream to run-loop,", self);
1353 NSLog (@"Error: %@", err);
1355 if (errPtr) *errPtr = err;
1358 for(i = 0; i < [theRunLoopModes count]; i++)
1360 CFReadStreamScheduleWithRunLoop(theReadStream, theRunLoop, (CFStringRef)[theRunLoopModes objectAtIndex:i]);
1363 // Make write stream non-blocking.
1364 if (!CFWriteStreamSetClient (theWriteStream,
1365 kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered | kCFStreamEventOpenCompleted,
1366 (CFWriteStreamClientCallBack)&MyCFWriteStreamCallback,
1367 (CFStreamClientContext *)(&theContext) ))
1369 NSError *err = [self getStreamError];
1371 NSLog (@"AsyncSocket %p couldn't attach write stream to run-loop,", self);
1372 NSLog (@"Error: %@", err);
1374 if (errPtr) *errPtr = err;
1378 for(i = 0; i < [theRunLoopModes count]; i++)
1380 CFWriteStreamScheduleWithRunLoop (theWriteStream, theRunLoop, (CFStringRef)[theRunLoopModes objectAtIndex:i]);
1387 * Allows the delegate method to configure the CFReadStream and/or CFWriteStream as desired before we connect.
1388 * Note that the CFSocket and CFNativeSocket will not be available until after the connection is opened.
1390 - (BOOL)configureStreamsAndReturnError:(NSError **)errPtr
1392 // Call the delegate method for further configuration.
1393 if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)])
1395 if([theDelegate onSocketWillConnect:self] == NO)
1397 if (errPtr) *errPtr = [self getAbortError];
1404 - (BOOL)openStreamsAndReturnError:(NSError **)errPtr
1408 if(pass && !CFReadStreamOpen (theReadStream))
1410 NSLog (@"AsyncSocket %p couldn't open read stream,", self);
1414 if(pass && !CFWriteStreamOpen (theWriteStream))
1416 NSLog (@"AsyncSocket %p couldn't open write stream,", self);
1422 if (errPtr) *errPtr = [self getStreamError];
1429 * Called when read or write streams open.
1430 * When the socket is connected and both streams are open, consider the AsyncSocket instance to be ready.
1432 - (void)doStreamOpen
1435 if ([self areStreamsConnected] && !(theFlags & kDidCallConnectDelegate))
1438 if (![self setSocketFromStreamsAndReturnError: &err])
1440 NSLog (@"AsyncSocket %p couldn't get socket from streams, %@. Disconnecting.", self, err);
1441 [self closeWithError:err];
1445 // Call the delegate.
1446 theFlags |= kDidCallConnectDelegate;
1447 if ([theDelegate respondsToSelector:@selector(onSocket:didConnectToHost:port:)])
1449 [theDelegate onSocket:self didConnectToHost:[self connectedHost] port:[self connectedPort]];
1452 // Immediately deal with any already-queued requests.
1453 [self maybeDequeueRead];
1454 [self maybeDequeueWrite];
1458 - (BOOL)setSocketFromStreamsAndReturnError:(NSError **)errPtr
1460 // Get the CFSocketNativeHandle from theReadStream
1461 CFSocketNativeHandle native;
1462 CFDataRef nativeProp = CFReadStreamCopyProperty(theReadStream, kCFStreamPropertySocketNativeHandle);
1463 if(nativeProp == NULL)
1465 if (errPtr) *errPtr = [self getStreamError];
1469 CFDataGetBytes(nativeProp, CFRangeMake(0, CFDataGetLength(nativeProp)), (UInt8 *)&native);
1470 CFRelease(nativeProp);
1472 CFSocketRef socket = CFSocketCreateWithNative(kCFAllocatorDefault, native, 0, NULL, NULL);
1475 if (errPtr) *errPtr = [self getSocketError];
1479 // Determine whether the connection was IPv4 or IPv6
1480 CFDataRef peeraddr = CFSocketCopyPeerAddress(socket);
1481 struct sockaddr *sa = (struct sockaddr *)CFDataGetBytePtr(peeraddr);
1483 if(sa->sa_family == AF_INET)
1489 theSocket6 = socket;
1492 CFRelease(peeraddr);
1497 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1498 #pragma mark Disconnect Implementation
1499 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1501 // Sends error message and disconnects
1502 - (void)closeWithError:(NSError *)err
1504 theFlags |= kClosingWithError;
1506 if (theFlags & kDidPassConnectMethod)
1508 // Try to salvage what data we can.
1509 [self recoverUnreadData];
1511 // Let the delegate know, so it can try to recover if it likes.
1512 if ([theDelegate respondsToSelector:@selector(onSocket:willDisconnectWithError:)])
1514 [theDelegate onSocket:self willDisconnectWithError:err];
1520 // Prepare partially read data for recovery.
1521 - (void)recoverUnreadData
1523 if((theCurrentRead != nil) && (theCurrentRead->bytesDone > 0))
1525 // We never finished the current read.
1526 // We need to move its data into the front of the partial read buffer.
1528 [partialReadBuffer replaceBytesInRange:NSMakeRange(0, 0)
1529 withBytes:[theCurrentRead->buffer bytes]
1530 length:theCurrentRead->bytesDone];
1538 if (theCurrentRead != nil) [self endCurrentRead];
1539 if (theCurrentWrite != nil) [self endCurrentWrite];
1540 [theReadQueue removeAllObjects];
1541 [theWriteQueue removeAllObjects];
1542 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueRead) object:nil];
1543 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueWrite) object:nil];
1547 * Disconnects. This is called for both error and clean disconnections.
1556 // Clear partialReadBuffer (pre-buffer and also unreadData buffer in case of error)
1557 [partialReadBuffer replaceBytesInRange:NSMakeRange(0, [partialReadBuffer length]) withBytes:NULL length:0];
1559 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(disconnect) object:nil];
1562 if (theReadStream != NULL)
1564 for(i = 0; i < [theRunLoopModes count]; i++)
1566 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
1567 CFReadStreamUnscheduleFromRunLoop(theReadStream, theRunLoop, runLoopMode);
1569 CFReadStreamSetClient(theReadStream, kCFStreamEventNone, NULL, NULL);
1570 CFReadStreamClose(theReadStream);
1571 CFRelease(theReadStream);
1572 theReadStream = NULL;
1574 if (theWriteStream != NULL)
1576 for(i = 0; i < [theRunLoopModes count]; i++)
1578 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
1579 CFWriteStreamUnscheduleFromRunLoop(theWriteStream, theRunLoop, runLoopMode);
1581 CFWriteStreamSetClient(theWriteStream, kCFStreamEventNone, NULL, NULL);
1582 CFWriteStreamClose(theWriteStream);
1583 CFRelease(theWriteStream);
1584 theWriteStream = NULL;
1588 if (theSocket != NULL)
1590 CFSocketInvalidate (theSocket);
1591 CFRelease (theSocket);
1594 if (theSocket6 != NULL)
1596 CFSocketInvalidate (theSocket6);
1597 CFRelease (theSocket6);
1600 if (theSource != NULL)
1602 for(i = 0; i < [theRunLoopModes count]; i++)
1604 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
1605 CFRunLoopRemoveSource(theRunLoop, theSource, runLoopMode);
1607 CFRelease (theSource);
1610 if (theSource6 != NULL)
1612 for(i = 0; i < [theRunLoopModes count]; i++)
1614 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
1615 CFRunLoopRemoveSource(theRunLoop, theSource6, runLoopMode);
1617 CFRelease (theSource6);
1622 // If the client has passed the connect/accept method, then the connection has at least begun.
1623 // Notify delegate that it is now ending.
1624 if (theFlags & kDidPassConnectMethod)
1626 // Delay notification to give dev freedom to release without returning here and core-dumping.
1627 if ([theDelegate respondsToSelector: @selector(onSocketDidDisconnect:)])
1629 [theDelegate performSelector:@selector(onSocketDidDisconnect:)
1632 inModes:theRunLoopModes];
1636 // Clear all flags (except the pre-buffering flag, which should remain as is)
1637 theFlags &= kEnablePreBuffering;
1641 * Disconnects immediately. Any pending reads or writes are dropped.
1649 * Diconnects after all pending reads have completed.
1651 - (void)disconnectAfterReading
1653 theFlags |= (kForbidReadsWrites | kDisconnectAfterReads);
1655 [self maybeScheduleDisconnect];
1659 * Disconnects after all pending writes have completed.
1661 - (void)disconnectAfterWriting
1663 theFlags |= (kForbidReadsWrites | kDisconnectAfterWrites);
1665 [self maybeScheduleDisconnect];
1669 * Disconnects after all pending reads and writes have completed.
1671 - (void)disconnectAfterReadingAndWriting
1673 theFlags |= (kForbidReadsWrites | kDisconnectAfterReads | kDisconnectAfterWrites);
1675 [self maybeScheduleDisconnect];
1679 * Schedules a call to disconnect if possible.
1680 * That is, if all writes have completed, and we're set to disconnect after writing,
1681 * or if all reads have completed, and we're set to disconnect after reading.
1683 - (void)maybeScheduleDisconnect
1685 BOOL shouldDisconnect = NO;
1687 if(theFlags & kDisconnectAfterReads)
1689 if(([theReadQueue count] == 0) && (theCurrentRead == nil))
1691 if(theFlags & kDisconnectAfterWrites)
1693 if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
1695 shouldDisconnect = YES;
1700 shouldDisconnect = YES;
1704 else if(theFlags & kDisconnectAfterWrites)
1706 if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
1708 shouldDisconnect = YES;
1712 if(shouldDisconnect)
1714 [self performSelector:@selector(disconnect) withObject:nil afterDelay:0 inModes:theRunLoopModes];
1719 * In the event of an error, this method may be called during onSocket:willDisconnectWithError: to read
1720 * any data that's left on the socket.
1722 - (NSData *)unreadData
1724 // Ensure this method will only return data in the event of an error
1725 if(!(theFlags & kClosingWithError)) return nil;
1727 if(theReadStream == NULL) return nil;
1729 CFIndex totalBytesRead = [partialReadBuffer length];
1731 while(!error && CFReadStreamHasBytesAvailable(theReadStream))
1733 [partialReadBuffer increaseLengthBy:READALL_CHUNKSIZE];
1735 // Number of bytes to read is space left in packet buffer.
1736 CFIndex bytesToRead = [partialReadBuffer length] - totalBytesRead;
1738 // Read data into packet buffer
1739 UInt8 *packetbuf = (UInt8 *)( [partialReadBuffer mutableBytes] + totalBytesRead );
1740 CFIndex bytesRead = CFReadStreamRead(theReadStream, packetbuf, bytesToRead);
1749 totalBytesRead += bytesRead;
1753 [partialReadBuffer setLength:totalBytesRead];
1755 return partialReadBuffer;
1758 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1760 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1763 * Returns a standard error object for the current errno value.
1764 * Errno is used for low-level BSD socket errors.
1766 - (NSError *)getErrnoError
1768 NSString *errorMsg = [NSString stringWithUTF8String:strerror(errno)];
1769 NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errorMsg forKey:NSLocalizedDescriptionKey];
1771 return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo];
1775 * Returns a standard error message for a CFSocket error.
1776 * Unfortunately, CFSocket offers no feedback on its errors.
1778 - (NSError *)getSocketError
1780 NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketCFSocketError",
1781 @"AsyncSocket", [NSBundle mainBundle],
1782 @"General CFSocket error", nil);
1784 NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
1786 return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketCFSocketError userInfo:info];
1789 - (NSError *) getStreamError
1792 if (theReadStream != NULL)
1794 err = CFReadStreamGetError (theReadStream);
1795 if (err.error != 0) return [self errorFromCFStreamError: err];
1798 if (theWriteStream != NULL)
1800 err = CFWriteStreamGetError (theWriteStream);
1801 if (err.error != 0) return [self errorFromCFStreamError: err];
1808 * Returns a standard AsyncSocket abort error.
1810 - (NSError *)getAbortError
1812 NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketCanceledError",
1813 @"AsyncSocket", [NSBundle mainBundle],
1814 @"Connection canceled", nil);
1816 NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
1818 return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketCanceledError userInfo:info];
1821 - (NSError *)getReadMaxedOutError
1823 NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketReadMaxedOutError",
1824 @"AsyncSocket", [NSBundle mainBundle],
1825 @"Read operation reached set maximum length", nil);
1827 NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
1829 return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketReadMaxedOutError userInfo:info];
1833 * Returns a standard AsyncSocket read timeout error.
1835 - (NSError *)getReadTimeoutError
1837 NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketReadTimeoutError",
1838 @"AsyncSocket", [NSBundle mainBundle],
1839 @"Read operation timed out", nil);
1841 NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
1843 return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketReadTimeoutError userInfo:info];
1847 * Returns a standard AsyncSocket write timeout error.
1849 - (NSError *)getWriteTimeoutError
1851 NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketWriteTimeoutError",
1852 @"AsyncSocket", [NSBundle mainBundle],
1853 @"Write operation timed out", nil);
1855 NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
1857 return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketWriteTimeoutError userInfo:info];
1860 - (NSError *)errorFromCFStreamError:(CFStreamError)err
1862 if (err.domain == 0 && err.error == 0) return nil;
1864 // Can't use switch; these constants aren't int literals.
1865 NSString *domain = @"CFStreamError (unlisted domain)";
1866 NSString *message = nil;
1868 if(err.domain == kCFStreamErrorDomainPOSIX) {
1869 domain = NSPOSIXErrorDomain;
1871 else if(err.domain == kCFStreamErrorDomainMacOSStatus) {
1872 domain = NSOSStatusErrorDomain;
1874 else if(err.domain == kCFStreamErrorDomainMach) {
1875 domain = NSMachErrorDomain;
1877 else if(err.domain == kCFStreamErrorDomainNetDB)
1879 domain = @"kCFStreamErrorDomainNetDB";
1880 message = [NSString stringWithCString:gai_strerror(err.error) encoding:NSASCIIStringEncoding];
1882 else if(err.domain == kCFStreamErrorDomainNetServices) {
1883 domain = @"kCFStreamErrorDomainNetServices";
1885 else if(err.domain == kCFStreamErrorDomainSOCKS) {
1886 domain = @"kCFStreamErrorDomainSOCKS";
1888 else if(err.domain == kCFStreamErrorDomainSystemConfiguration) {
1889 domain = @"kCFStreamErrorDomainSystemConfiguration";
1891 else if(err.domain == kCFStreamErrorDomainSSL) {
1892 domain = @"kCFStreamErrorDomainSSL";
1895 NSDictionary *info = nil;
1898 info = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey];
1900 return [NSError errorWithDomain:domain code:err.error userInfo:info];
1903 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1904 #pragma mark Diagnostics
1905 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1909 return [self isSocketConnected] && [self areStreamsConnected];
1912 - (NSString *)connectedHost
1915 return [self connectedHost:theSocket];
1917 return [self connectedHost:theSocket6];
1920 - (UInt16)connectedPort
1923 return [self connectedPort:theSocket];
1925 return [self connectedPort:theSocket6];
1928 - (NSString *)localHost
1931 return [self localHost:theSocket];
1933 return [self localHost:theSocket6];
1939 return [self localPort:theSocket];
1941 return [self localPort:theSocket6];
1944 - (NSString *)connectedHost:(CFSocketRef)socket
1946 if (socket == NULL) return nil;
1948 NSString *peerstr = nil;
1950 if(socket && (peeraddr = CFSocketCopyPeerAddress(socket)))
1952 peerstr = [self addressHost:peeraddr];
1953 CFRelease (peeraddr);
1959 - (UInt16)connectedPort:(CFSocketRef)socket
1961 if (socket == NULL) return 0;
1963 UInt16 peerport = 0;
1965 if(socket && (peeraddr = CFSocketCopyPeerAddress(socket)))
1967 peerport = [self addressPort:peeraddr];
1968 CFRelease (peeraddr);
1974 - (NSString *)localHost:(CFSocketRef)socket
1976 if (socket == NULL) return nil;
1978 NSString *selfstr = nil;
1980 if(socket && (selfaddr = CFSocketCopyAddress(socket)))
1982 selfstr = [self addressHost:selfaddr];
1983 CFRelease (selfaddr);
1989 - (UInt16)localPort:(CFSocketRef)socket
1991 if (socket == NULL) return 0;
1993 UInt16 selfport = 0;
1995 if (socket && (selfaddr = CFSocketCopyAddress(socket)))
1997 selfport = [self addressPort:selfaddr];
1998 CFRelease (selfaddr);
2004 - (BOOL)isSocketConnected
2006 if(theSocket != NULL)
2007 return CFSocketIsValid(theSocket);
2008 else if(theSocket6 != NULL)
2009 return CFSocketIsValid(theSocket6);
2014 - (BOOL)areStreamsConnected
2018 if (theReadStream != NULL)
2020 s = CFReadStreamGetStatus (theReadStream);
2021 if ( !(s == kCFStreamStatusOpen || s == kCFStreamStatusReading || s == kCFStreamStatusError) )
2026 if (theWriteStream != NULL)
2028 s = CFWriteStreamGetStatus (theWriteStream);
2029 if ( !(s == kCFStreamStatusOpen || s == kCFStreamStatusWriting || s == kCFStreamStatusError) )
2037 - (NSString *)addressHost:(CFDataRef)cfaddr
2039 if (cfaddr == NULL) return nil;
2041 char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
2042 struct sockaddr *pSockAddr = (struct sockaddr *) CFDataGetBytePtr (cfaddr);
2043 struct sockaddr_in *pSockAddrV4 = (struct sockaddr_in *) pSockAddr;
2044 struct sockaddr_in6 *pSockAddrV6 = (struct sockaddr_in6 *)pSockAddr;
2046 const void *pAddr = (pSockAddr->sa_family == AF_INET) ?
2047 (void *)(&(pSockAddrV4->sin_addr)) :
2048 (void *)(&(pSockAddrV6->sin6_addr));
2050 const char *pStr = inet_ntop (pSockAddr->sa_family, pAddr, addrBuf, sizeof(addrBuf));
2051 if (pStr == NULL) [NSException raise: NSInternalInconsistencyException
2052 format: @"Cannot convert address to string."];
2054 return [NSString stringWithCString:pStr encoding:NSASCIIStringEncoding];
2057 - (UInt16)addressPort:(CFDataRef)cfaddr
2059 if (cfaddr == NULL) return 0;
2060 struct sockaddr_in *pAddr = (struct sockaddr_in *) CFDataGetBytePtr (cfaddr);
2061 return ntohs (pAddr->sin_port);
2066 return (theSocket != NULL);
2071 return (theSocket6 != NULL);
2074 - (NSString *)description
2076 static const char *statstr[] = {"not open","opening","open","reading","writing","at end","closed","has error"};
2077 CFStreamStatus rs = (theReadStream != NULL) ? CFReadStreamGetStatus(theReadStream) : 0;
2078 CFStreamStatus ws = (theWriteStream != NULL) ? CFWriteStreamGetStatus(theWriteStream) : 0;
2080 NSString *peerstr, *selfstr;
2081 CFDataRef peeraddr = NULL, peeraddr6 = NULL, selfaddr = NULL, selfaddr6 = NULL;
2083 if (theSocket || theSocket6)
2085 if (theSocket) peeraddr = CFSocketCopyPeerAddress(theSocket);
2086 if (theSocket6) peeraddr6 = CFSocketCopyPeerAddress(theSocket6);
2088 if(theSocket6 && theSocket)
2090 peerstr = [NSString stringWithFormat: @"%@/%@ %u",
2091 [self addressHost:peeraddr], [self addressHost:peeraddr6], [self addressPort:peeraddr]];
2095 peerstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:peeraddr6], [self addressPort:peeraddr6]];
2099 peerstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:peeraddr], [self addressPort:peeraddr]];
2102 if(peeraddr) CFRelease(peeraddr);
2103 if(peeraddr6) CFRelease(peeraddr6);
2107 else peerstr = @"nowhere";
2109 if (theSocket || theSocket6)
2111 if (theSocket) selfaddr = CFSocketCopyAddress (theSocket);
2112 if (theSocket6) selfaddr6 = CFSocketCopyAddress (theSocket6);
2114 if (theSocket6 && theSocket)
2116 selfstr = [NSString stringWithFormat: @"%@/%@ %u",
2117 [self addressHost:selfaddr], [self addressHost:selfaddr6], [self addressPort:selfaddr]];
2119 else if (theSocket6)
2121 selfstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:selfaddr6], [self addressPort:selfaddr6]];
2125 selfstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:selfaddr], [self addressPort:selfaddr]];
2128 if(selfaddr) CFRelease(selfaddr);
2129 if(selfaddr6) CFRelease(selfaddr6);
2133 else selfstr = @"nowhere";
2135 NSMutableString *ms = [[NSMutableString alloc] initWithCapacity:150];
2137 [ms appendString:[NSString stringWithFormat:@"<AsyncSocket %p", self]];
2138 [ms appendString:[NSString stringWithFormat:@" local %@ remote %@ ", selfstr, peerstr]];
2140 unsigned readQueueCount = (unsigned)[theReadQueue count];
2141 unsigned writeQueueCount = (unsigned)[theWriteQueue count];
2143 [ms appendString:[NSString stringWithFormat:@"has queued %u reads %u writes, ", readQueueCount, writeQueueCount]];
2145 if (theCurrentRead == nil)
2146 [ms appendString: @"no current read, "];
2150 if ([theCurrentRead->buffer length] != 0)
2151 percentDone = (float)theCurrentRead->bytesDone /
2152 (float)[theCurrentRead->buffer length] * 100.0;
2156 [ms appendString: [NSString stringWithFormat:@"currently read %u bytes (%d%% done), ",
2157 (unsigned int)[theCurrentRead->buffer length],
2158 theCurrentRead->bytesDone ? percentDone : 0]];
2161 if (theCurrentWrite == nil)
2162 [ms appendString: @"no current write, "];
2166 if ([theCurrentWrite->buffer length] != 0)
2167 percentDone = (float)theCurrentWrite->bytesDone /
2168 (float)[theCurrentWrite->buffer length] * 100.0;
2172 [ms appendString: [NSString stringWithFormat:@"currently written %u (%d%%), ",
2173 (unsigned int)[theCurrentWrite->buffer length],
2174 theCurrentWrite->bytesDone ? percentDone : 0]];
2177 [ms appendString:[NSString stringWithFormat:@"read stream %p %s, ", theReadStream, statstr[rs]]];
2178 [ms appendString:[NSString stringWithFormat:@"write stream %p %s", theWriteStream, statstr[ws]]];
2180 if(theFlags & kDisconnectAfterReads)
2182 if(theFlags & kDisconnectAfterWrites)
2183 [ms appendString: @", will disconnect after reads & writes"];
2185 [ms appendString: @", will disconnect after reads"];
2187 else if(theFlags & kDisconnectAfterWrites)
2189 [ms appendString: @", will disconnect after writes"];
2192 if (![self isConnected]) [ms appendString: @", not connected"];
2194 [ms appendString:@">"];
2196 return [ms autorelease];
2199 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2200 #pragma mark Reading
2201 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2203 - (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
2205 if(length == 0) return;
2206 if(theFlags & kForbidReadsWrites) return;
2208 NSMutableData *buffer = [[NSMutableData alloc] initWithLength:length];
2209 AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
2216 [theReadQueue addObject:packet];
2217 [self scheduleDequeueRead];
2223 - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
2225 [self readDataToData:data withTimeout:timeout maxLength:-1 tag:tag];
2228 - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(CFIndex)length tag:(long)tag
2230 if(data == nil || [data length] == 0) return;
2231 if(length >= 0 && length < [data length]) return;
2232 if(theFlags & kForbidReadsWrites) return;
2234 NSMutableData *buffer = [[NSMutableData alloc] initWithLength:0];
2235 AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
2242 [theReadQueue addObject:packet];
2243 [self scheduleDequeueRead];
2249 - (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag
2251 if (theFlags & kForbidReadsWrites) return;
2253 NSMutableData *buffer = [[NSMutableData alloc] initWithLength:0];
2254 AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
2257 readAllAvailable:YES
2261 [theReadQueue addObject:packet];
2262 [self scheduleDequeueRead];
2269 * Puts a maybeDequeueRead on the run loop.
2270 * An assumption here is that selectors will be performed consecutively within their priority.
2272 - (void)scheduleDequeueRead
2274 [self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes];
2278 * This method starts a new read, if needed.
2279 * It is called when a user requests a read,
2280 * or when a stream opens that may have requested reads sitting in the queue, etc.
2282 - (void)maybeDequeueRead
2284 // If we're not currently processing a read AND we have an available read stream
2285 if((theCurrentRead == nil) && (theReadStream != NULL))
2287 if([theReadQueue count] > 0)
2289 // Dequeue the next object in the write queue
2290 theCurrentRead = [[theReadQueue objectAtIndex:0] retain];
2291 [theReadQueue removeObjectAtIndex:0];
2293 if([theCurrentRead isKindOfClass:[AsyncSpecialPacket class]])
2295 // Attempt to start TLS
2296 // This method won't do anything unless both theCurrentRead and theCurrentWrite are start TLS packets
2297 [self maybeStartTLS];
2301 // Start time-out timer
2302 if(theCurrentRead->timeout >= 0.0)
2304 theReadTimer = [NSTimer timerWithTimeInterval:theCurrentRead->timeout
2306 selector:@selector(doReadTimeout:)
2310 for(i = 0; i < [theRunLoopModes count]; i++)
2312 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
2313 CFRunLoopAddTimer(theRunLoop, (CFRunLoopTimerRef)theReadTimer, runLoopMode);
2317 // Immediately read, if possible
2318 [self doBytesAvailable];
2321 else if(theFlags & kDisconnectAfterReads)
2323 if(theFlags & kDisconnectAfterWrites)
2325 if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
2339 * Call this method in doBytesAvailable instead of CFReadStreamHasBytesAvailable().
2340 * This method supports pre-buffering properly.
2342 - (BOOL)hasBytesAvailable
2344 return ([partialReadBuffer length] > 0) || CFReadStreamHasBytesAvailable(theReadStream);
2348 * Call this method in doBytesAvailable instead of CFReadStreamRead().
2349 * This method support pre-buffering properly.
2351 - (CFIndex)readIntoBuffer:(UInt8 *)buffer maxLength:(CFIndex)length
2353 if([partialReadBuffer length] > 0)
2355 // Determine the maximum amount of data to read
2356 CFIndex bytesToRead = MIN(length, [partialReadBuffer length]);
2358 // Copy the bytes from the buffer
2359 memcpy(buffer, [partialReadBuffer bytes], bytesToRead);
2361 // Remove the copied bytes from the buffer
2362 [partialReadBuffer replaceBytesInRange:NSMakeRange(0, bytesToRead) withBytes:NULL length:0];
2368 return CFReadStreamRead(theReadStream, buffer, length);
2373 * This method is called when a new read is taken from the read queue or when new data becomes available on the stream.
2375 - (void)doBytesAvailable
2377 // If data is available on the stream, but there is no read request, then we don't need to process the data yet.
2378 // Also, if there is a read request, but no read stream setup yet, we can't process any data yet.
2379 if(theCurrentRead != nil && theReadStream != NULL)
2381 CFIndex totalBytesRead = 0;
2384 BOOL socketError = NO;
2385 BOOL maxoutError = NO;
2387 while(!done && !socketError && !maxoutError && [self hasBytesAvailable])
2389 BOOL didPreBuffer = NO;
2391 // If reading all available data, make sure there's room in the packet buffer.
2392 if(theCurrentRead->readAllAvailableData == YES)
2394 // Make sure there is at least READALL_CHUNKSIZE bytes available.
2395 // We don't want to increase the buffer any more than this or we'll waste space.
2396 // With prebuffering it's possible to read in a small chunk on the first read.
2398 unsigned buffInc = READALL_CHUNKSIZE - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
2399 [theCurrentRead->buffer increaseLengthBy:buffInc];
2402 // If reading until data, we may only want to read a few bytes.
2403 // Just enough to ensure we don't go past our term or over our max limit.
2404 // Unless pre-buffering is enabled, in which case we may want to read in a larger chunk.
2405 if(theCurrentRead->term != nil)
2407 // If we already have data pre-buffered, we obviously don't want to pre-buffer it again.
2408 // So in this case we'll just read as usual.
2410 if(([partialReadBuffer length] > 0) || !(theFlags & kEnablePreBuffering))
2412 unsigned maxToRead = [theCurrentRead readLengthForTerm];
2414 unsigned bufInc = maxToRead - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
2415 [theCurrentRead->buffer increaseLengthBy:bufInc];
2420 unsigned maxToRead = [theCurrentRead prebufferReadLengthForTerm];
2422 unsigned buffInc = maxToRead - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
2423 [theCurrentRead->buffer increaseLengthBy:buffInc];
2428 // Number of bytes to read is space left in packet buffer.
2429 CFIndex bytesToRead = [theCurrentRead->buffer length] - theCurrentRead->bytesDone;
2431 // Read data into packet buffer
2432 UInt8 *subBuffer = (UInt8 *)([theCurrentRead->buffer mutableBytes] + theCurrentRead->bytesDone);
2433 CFIndex bytesRead = [self readIntoBuffer:subBuffer maxLength:bytesToRead];
2442 // Update total amound read for the current read
2443 theCurrentRead->bytesDone += bytesRead;
2445 // Update total amount read in this method invocation
2446 totalBytesRead += bytesRead;
2450 if(theCurrentRead->readAllAvailableData != YES)
2452 if(theCurrentRead->term != nil)
2456 // Search for the terminating sequence within the big chunk we just read.
2457 CFIndex overflow = [theCurrentRead searchForTermAfterPreBuffering:bytesRead];
2461 // Copy excess data into partialReadBuffer
2462 NSMutableData *buffer = theCurrentRead->buffer;
2463 const void *overflowBuffer = [buffer bytes] + theCurrentRead->bytesDone - overflow;
2465 [partialReadBuffer appendBytes:overflowBuffer length:overflow];
2467 // Update the bytesDone variable.
2468 // Note: The completeCurrentRead method will trim the buffer for us.
2469 theCurrentRead->bytesDone -= overflow;
2472 done = (overflow >= 0);
2476 // Search for the terminating sequence at the end of the buffer
2477 int termlen = [theCurrentRead->term length];
2478 if(theCurrentRead->bytesDone >= termlen)
2480 const void *buf = [theCurrentRead->buffer bytes] + (theCurrentRead->bytesDone - termlen);
2481 const void *seq = [theCurrentRead->term bytes];
2482 done = (memcmp (buf, seq, termlen) == 0);
2486 if(!done && theCurrentRead->maxLength >= 0 && theCurrentRead->bytesDone >= theCurrentRead->maxLength)
2488 // There's a set maxLength, and we've reached that maxLength without completing the read
2494 // Done when (sized) buffer is full.
2495 done = ([theCurrentRead->buffer length] == theCurrentRead->bytesDone);
2498 // else readAllAvailable doesn't end until all readable is read.
2501 if(theCurrentRead->readAllAvailableData && theCurrentRead->bytesDone > 0)
2502 done = YES; // Ran out of bytes, so the "read-all-data" type packet is done
2506 [self completeCurrentRead];
2507 if (!socketError) [self scheduleDequeueRead];
2509 else if(theCurrentRead->bytesDone > 0)
2511 // We're not done with the readToLength or readToData yet, but we have read in some bytes
2512 if ([theDelegate respondsToSelector:@selector(onSocket:didReadPartialDataOfLength:tag:)])
2514 [theDelegate onSocket:self didReadPartialDataOfLength:totalBytesRead tag:theCurrentRead->tag];
2520 CFStreamError err = CFReadStreamGetError(theReadStream);
2521 [self closeWithError:[self errorFromCFStreamError:err]];
2526 [self closeWithError:[self getReadMaxedOutError]];
2532 // Ends current read and calls delegate.
2533 - (void)completeCurrentRead
2535 NSAssert (theCurrentRead, @"Trying to complete current read when there is no current read.");
2537 [theCurrentRead->buffer setLength:theCurrentRead->bytesDone];
2538 if([theDelegate respondsToSelector:@selector(onSocket:didReadData:withTag:)])
2540 [theDelegate onSocket:self didReadData:theCurrentRead->buffer withTag:theCurrentRead->tag];
2543 if (theCurrentRead != nil) [self endCurrentRead]; // Caller may have disconnected.
2546 // Ends current read.
2547 - (void)endCurrentRead
2549 NSAssert (theCurrentRead, @"Trying to end current read when there is no current read.");
2551 [theReadTimer invalidate];
2554 [theCurrentRead release];
2555 theCurrentRead = nil;
2558 - (void)doReadTimeout:(NSTimer *)timer
2560 if (timer != theReadTimer) return; // Old timer. Ignore it.
2561 if (theCurrentRead != nil)
2563 [self endCurrentRead];
2565 [self closeWithError:[self getReadTimeoutError]];
2568 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2569 #pragma mark Writing
2570 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2572 - (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
2574 if (data == nil || [data length] == 0) return;
2575 if (theFlags & kForbidReadsWrites) return;
2577 AsyncWritePacket *packet = [[AsyncWritePacket alloc] initWithData:data timeout:timeout tag:tag];
2579 [theWriteQueue addObject:packet];
2580 [self scheduleDequeueWrite];
2585 - (void)scheduleDequeueWrite
2587 [self performSelector:@selector(maybeDequeueWrite) withObject:nil afterDelay:0 inModes:theRunLoopModes];
2590 // Start a new write.
2591 - (void)maybeDequeueWrite
2593 // If we're not currently processing a write AND we have an available write stream
2594 if((theCurrentWrite == nil) && (theWriteStream != NULL))
2596 if([theWriteQueue count] > 0)
2598 // Dequeue the next object in the write queue
2599 theCurrentWrite = [[theWriteQueue objectAtIndex:0] retain];
2600 [theWriteQueue removeObjectAtIndex:0];
2602 if([theCurrentWrite isKindOfClass:[AsyncSpecialPacket class]])
2604 // Attempt to start TLS
2605 // This method won't do anything unless both theCurrentWrite and theCurrentRead are start TLS packets
2606 [self maybeStartTLS];
2610 // Start time-out timer
2611 if (theCurrentWrite->timeout >= 0.0)
2613 theWriteTimer = [NSTimer timerWithTimeInterval:theCurrentWrite->timeout
2615 selector:@selector(doWriteTimeout:)
2619 for(i = 0; i < [theRunLoopModes count]; i++)
2621 CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
2622 CFRunLoopAddTimer(theRunLoop, (CFRunLoopTimerRef)theWriteTimer, runLoopMode);
2626 // Immediately write, if possible
2630 else if(theFlags & kDisconnectAfterWrites)
2632 if(theFlags & kDisconnectAfterReads)
2634 if(([theReadQueue count] == 0) && (theCurrentRead == nil))
2649 if (theCurrentWrite != nil && theWriteStream != NULL)
2651 BOOL done = NO, error = NO;
2652 while (!done && !error && CFWriteStreamCanAcceptBytes (theWriteStream))
2654 // Figure out what to write.
2655 CFIndex bytesRemaining = [theCurrentWrite->buffer length] - theCurrentWrite->bytesDone;
2656 CFIndex bytesToWrite = (bytesRemaining < WRITE_CHUNKSIZE) ? bytesRemaining : WRITE_CHUNKSIZE;
2657 UInt8 *writestart = (UInt8 *)([theCurrentWrite->buffer bytes] + theCurrentWrite->bytesDone);
2660 CFIndex bytesWritten = CFWriteStreamWrite (theWriteStream, writestart, bytesToWrite);
2663 if (bytesWritten < 0)
2670 theCurrentWrite->bytesDone += bytesWritten;
2671 done = ([theCurrentWrite->buffer length] == theCurrentWrite->bytesDone);
2676 [self completeCurrentWrite];
2677 if (!error) [self scheduleDequeueWrite];
2682 CFStreamError err = CFWriteStreamGetError (theWriteStream);
2683 [self closeWithError: [self errorFromCFStreamError:err]];
2689 // Ends current write and calls delegate.
2690 - (void)completeCurrentWrite
2692 NSAssert (theCurrentWrite, @"Trying to complete current write when there is no current write.");
2694 if ([theDelegate respondsToSelector:@selector(onSocket:didWriteDataWithTag:)])
2696 [theDelegate onSocket:self didWriteDataWithTag:theCurrentWrite->tag];
2699 if (theCurrentWrite != nil) [self endCurrentWrite]; // Caller may have disconnected.
2702 // Ends current write.
2703 - (void)endCurrentWrite
2705 NSAssert (theCurrentWrite, @"Trying to complete current write when there is no current write.");
2707 [theWriteTimer invalidate];
2708 theWriteTimer = nil;
2710 [theCurrentWrite release];
2711 theCurrentWrite = nil;
2714 - (void)doWriteTimeout:(NSTimer *)timer
2716 if(timer != theWriteTimer)
2718 // Old timer - Ignore it
2721 if(theCurrentWrite != nil)
2723 [self endCurrentWrite];
2725 [self closeWithError:[self getWriteTimeoutError]];
2728 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2729 #pragma mark Security
2730 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2732 - (void)startTLS:(NSDictionary *)tlsSettings
2734 if([tlsSettings count] == 0) return;
2736 AsyncSpecialPacket *packet = [[AsyncSpecialPacket alloc] initWithTLSSettings:tlsSettings];
2738 [theReadQueue addObject:packet];
2739 [self scheduleDequeueRead];
2741 [theWriteQueue addObject:packet];
2742 [self scheduleDequeueWrite];
2747 - (void)maybeStartTLS
2749 NSAssert((theFlags & kStartingTLS) == 0, @"Trying to start TLS after TLS has already been started");
2751 // We can't start TLS until:
2752 // - All queued reads prior to the user calling StartTLS are complete
2753 // - All queued writes prior to the user calling StartTLS are complete
2755 // We'll know these conditions are met when both theCurrentRead and theCurrentWrite variables
2756 // are of type AsyncSpecialPacket.
2758 Class SpecialPacketClass = [AsyncSpecialPacket class];
2760 if([theCurrentRead isKindOfClass:SpecialPacketClass] && [theCurrentWrite isKindOfClass:SpecialPacketClass])
2762 theFlags |= kStartingTLS;
2764 AsyncSpecialPacket *tlsPacket = (AsyncSpecialPacket *)theCurrentRead;
2766 BOOL didSecureReadStream = CFReadStreamSetProperty(theReadStream, kCFStreamPropertySSLSettings,
2767 (CFDictionaryRef)tlsPacket->tlsSettings);
2768 BOOL didSecureWriteStream = CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertySSLSettings,
2769 (CFDictionaryRef)tlsPacket->tlsSettings);
2771 if(!didSecureReadStream || !didSecureWriteStream)
2773 [self onTLSStarted:NO];
2778 - (void)onTLSStarted:(BOOL)flag
2780 theFlags &= ~kStartingTLS;
2782 if([theDelegate respondsToSelector:@selector(onSocket:didSecure:)])
2784 [theDelegate onSocket:self didSecure:flag];
2787 [self endCurrentRead];
2788 [self endCurrentWrite];
2790 [self scheduleDequeueRead];
2791 [self scheduleDequeueWrite];
2794 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2795 #pragma mark CF Callbacks
2796 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2798 - (void)doCFSocketCallback:(CFSocketCallBackType)type
2799 forSocket:(CFSocketRef)sock
2800 withAddress:(NSData *)address
2801 withData:(const void *)pData
2803 NSParameterAssert ((sock == theSocket) || (sock == theSocket6));
2807 case kCFSocketConnectCallBack:
2808 // The data argument is either NULL or a pointer to an SInt32 error code, if the connect failed.
2810 [self doSocketOpen:sock withCFSocketError:kCFSocketError];
2812 [self doSocketOpen:sock withCFSocketError:kCFSocketSuccess];
2814 case kCFSocketAcceptCallBack:
2815 [self doAcceptWithSocket: *((CFSocketNativeHandle *)pData)];
2818 NSLog (@"AsyncSocket %p received unexpected CFSocketCallBackType %d.", self, type);
2823 - (void)doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream
2825 NSParameterAssert(theReadStream != NULL);
2830 case kCFStreamEventOpenCompleted:
2831 [self doStreamOpen];
2833 case kCFStreamEventHasBytesAvailable:
2834 if(theFlags & kStartingTLS)
2835 [self onTLSStarted:YES];
2837 [self doBytesAvailable];
2839 case kCFStreamEventErrorOccurred:
2840 case kCFStreamEventEndEncountered:
2841 err = CFReadStreamGetError (theReadStream);
2842 [self closeWithError: [self errorFromCFStreamError:err]];
2845 NSLog (@"AsyncSocket %p received unexpected CFReadStream callback, CFStreamEventType %d.", self, type);
2849 - (void)doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream
2851 NSParameterAssert(theWriteStream != NULL);
2856 case kCFStreamEventOpenCompleted:
2857 [self doStreamOpen];
2859 case kCFStreamEventCanAcceptBytes:
2860 if(theFlags & kStartingTLS)
2861 [self onTLSStarted:YES];
2865 case kCFStreamEventErrorOccurred:
2866 case kCFStreamEventEndEncountered:
2867 err = CFWriteStreamGetError (theWriteStream);
2868 [self closeWithError: [self errorFromCFStreamError:err]];
2871 NSLog (@"AsyncSocket %p received unexpected CFWriteStream callback, CFStreamEventType %d.", self, type);
2876 * This is the callback we setup for CFSocket.
2877 * This method does nothing but forward the call to it's Objective-C counterpart
2879 static void MyCFSocketCallback (CFSocketRef sref, CFSocketCallBackType type, CFDataRef address, const void *pData, void *pInfo)
2881 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2883 AsyncSocket *socket = [[(AsyncSocket *)pInfo retain] autorelease];
2884 [socket doCFSocketCallback:type forSocket:sref withAddress:(NSData *)address withData:pData];
2890 * This is the callback we setup for CFReadStream.
2891 * This method does nothing but forward the call to it's Objective-C counterpart
2893 static void MyCFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo)
2895 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2897 AsyncSocket *socket = [[(AsyncSocket *)pInfo retain] autorelease];
2898 [socket doCFReadStreamCallback:type forStream:stream];
2904 * This is the callback we setup for CFWriteStream.
2905 * This method does nothing but forward the call to it's Objective-C counterpart
2907 static void MyCFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType type, void *pInfo)
2909 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2911 AsyncSocket *socket = [[(AsyncSocket *)pInfo retain] autorelease];
2912 [socket doCFWriteStreamCallback:type forStream:stream];
2917 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2918 #pragma mark Class Methods
2919 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2921 // Return line separators.
2922 + (NSData *)CRLFData
2924 return [NSData dataWithBytes:"\x0D\x0A" length:2];
2929 return [NSData dataWithBytes:"\x0D" length:1];
2934 return [NSData dataWithBytes:"\x0A" length:1];
2937 + (NSData *)ZeroData
2939 return [NSData dataWithBytes:"" length:1];