Take the 2-minute tour ×

This is a sibling question of Asynchronous IO with CFWriteStream. I'm using CFReadStreamScheduleWithRunLoop and CFReadSteamRead to do asynchronous IO. How can we safely retrieve all the date with blocking?

Let's say the actual size of a message was 1200 (but we don't know), and the size of my read buffer was 1024. A call to CFReadStreamRead will retrieve up to 1024 bytes of data, but since we don't know the size of the message, we should call CFReadStreamRead repeatedly. The problem is that since we don't know how much of data the stream socket has received, the CFReadStreamRead might block from the second call. How can we avoid this problem?


share|improve this question
To make the point clear: When I get a kCFStreamEventHasBytesAvailable notification as the document says, the first call to CFReadStreamRead would be safe. But we have no idea whether the reason CFReadStreamRead returned was because the buffer wasn't big enough or the stream socket didn't have more data. And if all the 1200 bytes of data had been received before the first notification, we might not get additional notifications for the remaining data. So we should make a second call to CFReadStreamRead and there's no guarantee that the second call wouldn't block. Is the assumption correct? –  Kay Jul 28 '10 at 7:42
add comment

2 Answers

Call again CFReadStreamHasBytesAvailable() on your stream to see if it's still safe to read from it (or if the only way to know is to try).

share|improve this answer
add comment

I've added a sample code, that did the job for me. It's using CFReadStreamHasBytesAvailable(). It's important to check for the status of the stream, otherwise you might end up in an endless loop. My example also includes a timeout handling.

NSMutableData* bodyData = [NSMutableData dataWithCapacity:kHTTPReadStreamBufferSize];
    NSDate* startTimeStamp = [NSDate date];

    while (TRUE) {
        if (CFReadStreamHasBytesAvailable(httpReadStream)) {
            startTimeStamp = [NSDate date];
            UInt8* streambuffer = malloc(kHTTPReadStreamBufferSize);
            int readBytes = CFReadStreamRead (httpReadStream,streambuffer,kHTTPReadStreamBufferSize);
            NSLog(@"Read: %d",readBytes);
            [bodyData appendBytes:streambuffer length:readBytes];

        if (CFReadStreamGetStatus(httpReadStream) == kCFStreamStatusError) {
            *error = (NSError*)CFReadStreamCopyError (httpReadStream);
            if ([*error code] == 61) {
                NSLog(@"Error occured: %d",[*error code]);
                // connection refused
                [PlusError errorForDomainWithCode:kPlusHostUnreachable errorDescription:NSLocalizedString(@"kPlusHostUnreachable",@"")
                                  underlyingError:nil url:nil toError:error];

            *responseHeader = nil;
            *bodyContent = nil;

        if (CFReadStreamGetStatus(httpReadStream) == kCFStreamStatusAtEnd) {
            NSLog(@"Stream reached end!");
            *responseHeader = (CFHTTPMessageRef)CFReadStreamCopyProperty(httpReadStream, kCFStreamPropertyHTTPResponseHeader);      
            *error = nil;

        // timeout management
        NSTimeInterval timeInterval = [startTimeStamp timeIntervalSinceNow]*-1;
        if (timeInterval >= kHTTPReadTimeOutTimeInSeconds) {
            [PlusError errorForDomainWithCode:kPlusResourceLoadingError errorDescription:NSLocalizedString(@"kPlusResourceLoadingError",@"") 
                              underlyingError:nil url:nil toError:error];

share|improve this answer
add comment

Your Answer


