Search code examples
objective-ccore-foundation

Detecting connection errors when using CFStreamCreatePairWithSocketToCFHost


I am finding the doc for CFStreamCreatePairWithSocketToCFHost confusing:

Specifically, its not clear to me how the function can set the readStream pointer to null on error. as far as I understand, the pointer is passed by value - so the function can only change the objected pointed to by the pointer. Right now I can't figure out how to detect connection errors.

Relevant doc snippet:


Creates readable and writable streams connected to a given CFHost object.

void CFStreamCreatePairWithSocketToCFHost (
   CFAllocatorRef alloc,
   CFHostRef host,
   SInt32 port,
   CFReadStreamRef *readStream,
   CFWriteStreamRef *writeStream
);

readStream

Upon return, contains a CFReadStream object connected to the host host on port port, or NULL if there is a failure during creation. If you pass NULL, the function will not create a readable stream. Ownership follows the Create Rule.


This is my connecting code, it goes all the way to NSLog(@"Connected") even when the server is down.

NSLog(@"Attempting to (re)connect to %@:%d", m_host, m_port);
while(TRUE)
{
    CFHostRef host = CFHostCreateWithName(kCFAllocatorDefault, (CFStringRef)m_host);
    if (!host)
    {
        NSLog(@"Error resolving host %@", m_host);
        [NSThread sleepForTimeInterval:5.0];
        continue;
    }
    CFStreamCreatePairWithSocketToCFHost(kCFAllocatorDefault, host , m_port, &m_in, &m_out);
    CFRelease(host);

    if (!m_in)
    {
        NSLog(@"Error");
    }

    CFStreamClientContext context = {0, self,nil,nil,nil};

    if (CFReadStreamSetClient(m_in, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, networkReadEvent, &context))
    {
        CFReadStreamScheduleWithRunLoop(m_in, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
    }

    if (CFWriteStreamSetClient(m_out, kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, networkWriteEvent, &context))
    {
        CFWriteStreamScheduleWithRunLoop(m_out, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
    }


    BOOL success = CFReadStreamOpen(m_in);
    CFErrorRef error = CFReadStreamCopyError(m_in);
    if (!success || (error && CFErrorGetCode(error) != 0))
    {
        NSLog(@"Connect error %s : %d", CFErrorGetDomain(error), CFErrorGetCode(error));
        [NSThread sleepForTimeInterval:5.0];
    }
    else 
    {
        NSLog(@"Connected");
        break;
    }
}

Solution

  • From the "CFNetwork Programming Guide":

    Opening a stream can be a lengthy process, so the CFReadStreamOpen and CFWriteStreamOpen functions avoid blocking by returning TRUE to indicate that the process of opening the stream has begun. To check the status of the open, call the functions CFReadStreamGetStatus and CFWriteStreamGetStatus, which returnkCFStreamStatusOpening if the open is still in progress, kCFStreamStatusOpen if the open is complete, orkCFStreamStatusErrorOccurred if the open has completed but failed. In most cases, it doesn’t matter whether the open is complete because the CFStream functions that read and write will block until the stream is open.

    Also check out the kCFStreamEventOpenCompleted, (http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFStreamConstants/Reference/reference.html) : a stream event that reports the successful completion of the opening process. So to conclude, after calling CFReadStreamOpen (or Write), which will probably succeed, register to listen to the "OpenCompleted" event to identify a "real" success.