Asynchronous IO with CFReadStream
This is a sibling que开发者_如何学编程stion 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?
Thanks!
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).
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];
free(streambuffer);
}
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;
break;
}
if (CFReadStreamGetStatus(httpReadStream) == kCFStreamStatusAtEnd) {
NSLog(@"Stream reached end!");
*responseHeader = (CFHTTPMessageRef)CFReadStreamCopyProperty(httpReadStream, kCFStreamPropertyHTTPResponseHeader);
*error = nil;
break;
}
// timeout management
NSTimeInterval timeInterval = [startTimeStamp timeIntervalSinceNow]*-1;
if (timeInterval >= kHTTPReadTimeOutTimeInSeconds) {
[PlusError errorForDomainWithCode:kPlusResourceLoadingError errorDescription:NSLocalizedString(@"kPlusResourceLoadingError",@"")
underlyingError:nil url:nil toError:error];
break;
}
}
精彩评论