转自:http://www.opensource.apple.com/source/CFNetwork/CFNetwork-129.9/Stream/CFSocketStream.c
/* static */ Boolean
_SocketStreamAttemptNextConnection_NoLock(_CFSocketStreamContext* ctxt) {
do {
/* Attempt to get the primary host for connecting */
CFTypeRef lookup = (CFTypeRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyHostForOpen);
SInt32* attempt = NULL;
/* If there was a host, there is more work to do */
if (lookup) {
CFIndex count;
CFArrayRef list = NULL;
CFDataRef address = NULL;
CFNumberRef port = _CFNumberCopyPortForOpen(ctxt->_properties);
CFMutableDataRef a = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertySocketAddressAttempt);
/* If there is an address attempt, point to the counter. */
if (a)
attempt = (SInt32*)CFDataGetMutableBytePtr(a);
/* This is the first attempt so create and add the counter. */
else {
SInt32 i = 0;
/* Create the counter. */
a = CFDataCreateMutable(CFGetAllocator(ctxt->_properties), sizeof(i));
/* If it fails, set out of memory and bail. */
if (!a) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
/* Not needed anymore. */
if (port) CFRelease(port);
break;
}
/* Add the attempt counter to the properties for later */
CFDictionaryAddValue(ctxt->_properties, _kCFStreamPropertySocketAddressAttempt, a);
CFRelease(a);
/* Point the attempt at the counter */
attempt = (SInt32*)CFDataGetMutableBytePtr(a);
/* Start counting at zero. */
*attempt = 0;
}
/* Get the address list from the lookup. */
if (CFGetTypeID(lookup) == CFHostGetTypeID())
list = CFHostGetAddressing((CFHostRef)lookup, NULL);
else
list = CFNetServiceGetAddressing((CFNetServiceRef)lookup);
/* If there were no addresses, return an error. */
if (!list || (*attempt >= (count = CFArrayGetCount(list)))) {
if (!ctxt->_error.error) {
ctxt->_error.error = EAI_NODATA;
ctxt->_error.domain = kCFStreamErrorDomainNetDB;
}
/* Not needed anymore. */
if (port) CFRelease(port);
break;
}
/* Go through the list until a usable address is found */
do {
/* Create the address for connecting. */
address = _CFDataCopyAddressByInjectingPort((CFDataRef)CFArrayGetValueAtIndex(list, *attempt), port);
/* The next attempt will be the next address in the list. */
*attempt = *attempt + 1;
/* Only try to connect if there is an address */
if (address) {
/* If there was a socket, only need to connect it. */
if (ctxt->_socket) {
/*
** If a socket was created previously, there is only one attempt
** since the required socket type and protocol aren't known.
*/
*attempt = count;
/* Start the connection. */
_SocketStreamConnect_NoLock(ctxt, address);
}
/* Try to create and start connecting to the address */
else if (_SocketStreamCreateSocket_NoLock(ctxt, address))
_SocketStreamConnect_NoLock(ctxt, address);
/* No longer need the address */
CFRelease(address);
/* If succeeded in starting connect, don't continue anymore. */
if (!ctxt->_error.error) {
/* Not needed anymore. */
if (port) CFRelease(port);
return TRUE; /* NOTE the early return here. */
}
}
/* Continue through the list until all are exhausted. */
} while (*attempt < count);
/* Not needed anymore. */
if (port) CFRelease(port);
/*
** It's an error to get to this point. It means that none
** of the addresses were suitable or worked.
*/
if (!ctxt->_error.error) {
ctxt->_error.error = EINVAL;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
}
break;
}
/* If there is no lookup and no socket, something is bad. */
else {
int i;
int yes = 1;
CFOptionFlags flags;
CFBooleanRef boolean;
CFSocketNativeHandle s;
CFSocketContext c = {0, ctxt, NULL, NULL, NULL};
CFArrayRef loops[3] = {ctxt->_readloops, ctxt->_writeloops, ctxt->_sharedloops};
if (!ctxt->_socket) {
/* Try to get the native socket for creation. */
CFDataRef wrapper = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketNativeHandle);
if (!wrapper) {
ctxt->_error.error = EINVAL;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
break;
}
/* Create the CFSocket for riding. */
ctxt->_socket = CFSocketCreateWithNative(CFGetAllocator(ctxt->_properties),
*((CFSocketNativeHandle*)CFDataGetBytePtr(wrapper)),
kSocketEvents,
(CFSocketCallBack)_SocketCallBack,
&c);
if (!ctxt->_socket) {
/*
** Try to pull any error that may have just occurred. If none,
** assume an out of memory occurred.
*/
if (!_LastError(&ctxt->_error)) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
}
break;
}
/* Remove the cached value so it's only created when the client asks for it. */
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySocketNativeHandle);
}
/*
** No host lookup and a socket means that the streams were
** created with a connected socket already.
*/
__CFBitSet(ctxt->_flags, kFlagBitOpenComplete);
__CFBitClear(ctxt->_flags, kFlagBitPollOpen);
/* Get the native socket for setting options. */
s = CFSocketGetNative(ctxt->_socket);
#if !defined(__WIN32)
/* Turn off SIGPIPE on the socket (SIGPIPE doesn't exist on WIN32) */
setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(yes));
#endif
/* Place the socket in nonblocking mode. */
ioctl(s, FIONBIO, (void*)&yes);
/* Get the current socket flags and turn off the auto re-enable for reads and writes. */
flags = CFSocketGetSocketFlags(ctxt->_socket) &
~kCFSocketAutomaticallyReenableReadCallBack &
~kCFSocketAutomaticallyReenableWriteCallBack;
/* Find out if CFSocket should close the socket on invalidation. */
boolean = (CFBooleanRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyShouldCloseNativeSocket);
/* Adjust the flags on the setting. No value is the default which means to close. */
if (!boolean || (boolean != kCFBooleanFalse))
flags |= kCFSocketCloseOnInvalidate;
else
flags &= ~kCFSocketCloseOnInvalidate;
/* Set up the correct flags and enable the callbacks. */
CFSocketSetSocketFlags(ctxt->_socket, flags);
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack | kCFSocketWriteCallBack);
/* Now schedule the socket on all loops and modes */
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
_CFTypeScheduleOnMultipleRunLoops(ctxt->_socket, loops[i]);
/* Succeeded so make sure the socket is in the list of schedulables for future. */
_SchedulablesAdd(ctxt->_schedulables, ctxt->_socket);
}
return TRUE;
} while (0);
return FALSE;
}