Wednesday, April 27, 2016

iOS preventing blocking when working with streams

Problem is that when working with CFStreams, the socket reading could take longer time. If implement streams synchronously, entire stream will have to wait. 
There are two ways to avoid this 

1. Using run loop - Register to receive stream-related events and schedule stream on a run loop. When stream related event occurs, the callback function (specified by the registration call) is called. 

2. Polling - For read streams, find out if there are bytes to read before reading from the stream. For write streams, find out whether the stream can be written without blocking before writing to the stream. 

Using run loop to prevent blocking: This can be done by calling CFReadStreamScheduleWithRunLoop 

int makeRequest(const char *requestURL)
{
    CFReadStreamRef readStream;
    CFHTTPMessageRef request;
    CFStreamClientContext CTX = { 0, NULL, NULL, NULL, NULL };
    
    NSString* requestURLString = [ [ NSString alloc ] initWithCString:requestURL ];
    NSURL *url = [ NSURL URLWithString: requestURLString ];
    
    CFStringRef requestMessage = CFSTR("");
    
    request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"),
                                         (__bridge CFURLRef) url, kCFHTTPVersion1_1);
    if (!request) {
        return -1;
    }
    CFHTTPMessageSetBody(request, (CFDataRef) requestMessage);
    readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request);
    CFRelease(request);
    
    if (!readStream) {
        return -1;
    }
    
    if (!CFReadStreamSetClient(readStream, kCFStreamEventOpenCompleted |
                               kCFStreamEventHasBytesAvailable |
                               kCFStreamEventEndEncountered |
                               kCFStreamEventErrorOccurred,
                               readCallBack, &CTX))
    {
        CFRelease(readStream);
        return -1;
    }
    
    /* Add to the run loop */
    CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(),
                                    kCFRunLoopCommonModes);
    
    if (!CFReadStreamOpen(readStream)) {
        CFReadStreamSetClient(readStream, 0, NULL, NULL);
        CFReadStreamUnscheduleFromRunLoop(readStream,
                                          CFRunLoopGetCurrent(),
                                          kCFRunLoopCommonModes);
        CFRelease(readStream);
        return -1;
    }
    
    return 0;
}


void readCallBack(
                  CFReadStreamRef stream,
                  CFStreamEventType eventType,
                  void *clientCallBackInfo)
{
    UInt8 buffer[2048];
    CFIndex bytes_recvd = 0;
    
    NSLog(@"Stream event type :%lu",eventType);
    if(eventType == kCFStreamEventOpenCompleted ||
       eventType == kCFStreamEventHasBytesAvailable)
    {
        bytes_recvd = CFReadStreamRead(stream, buffer, sizeof(buffer));
        totalRead += bytes_recvd;
        if (bytes_recvd > 0)
        {
            NSLog(@"read bytes :%ld, total %d",bytes_recvd,totalRead);
        }
    }
    else if(eventType == kCFStreamEventErrorOccurred)
    {
        NSLog(@"Error Encountered :%d",totalRead);
    }
    else if(eventType == kCFStreamEventEndEncountered)
    {
        NSLog(@"End Encountered :%d",totalRead);
    }
}


references:

No comments:

Post a Comment