Stream to Get Data - NSInputStream

Baub picture Baub · Jul 13, 2011 · Viewed 20.2k times · Source

All,

I have a server that has a tcp socket stream for communication. I need to get to that stream and read the initial data that it needs to send me.

My current code is as follows. To be honest, I'm going at this completely blind. I'm not sure if this is correct let alone the right thing for the job.

-(void) initNetworkCommunication
{
    //input stream
    NSInputStream *iStream;
    NSURL *url = [url initWithString:@"192.168.17.1:2004"];

    [iStream initWithURL:url];
    [iStream setDelegate:self];
    [iStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [iStream open];

}

So from the way I see it, this code initializes the stream, but how do I read from the stream?

Thanks

Answer

ughoavgfhw picture ughoavgfhw · Jul 14, 2011

There are two ways to get data from a stream: polling and using stream events.

Polling is simpler, but will block the thread it is running in. If you use this method, you don't need to perform the setDelegate: or scheduleInRunLoop:forMode: calls. Polling is performed by repeatedly calling read:maxLength:.

NSInteger result;
uint8_t buffer[BUFFER_LEN]; // BUFFER_LEN can be any positive integer
while((result = [iStream read:buffer maxLength:BUFFER_LEN]) != 0) {
    if(result > 0) {
        // buffer contains result bytes of data to be handled
    } else {
        // The stream had an error. You can get an NSError object using [iStream streamError]
    }
}
// Either the stream ran out of data or there was an error

Using stream events requires setting the delegate and adding the stream to a run loop. Instead of blocking the thread, the stream will send a stream:handleEvent: message to its delegate when certain events occur, including when it receives data. The delegate can then retrieve the data from the stream. Here is an example stream:handleEvent: method:

- (void)stream:(NSInputStream *)iStream handleEvent:(NSStreamEvent)event {
    BOOL shouldClose = NO;
    switch(event) {
        case  NSStreamEventEndEncountered:
            shouldClose = YES;
            // If all data hasn't been read, fall through to the "has bytes" event
            if(![iStream hasBytesAvailable]) break;
        case NSStreamEventHasBytesAvailable: ; // We need a semicolon here before we can declare local variables
            uint8_t *buffer;
            NSUInteger length;
            BOOL freeBuffer = NO;
            // The stream has data. Try to get its internal buffer instead of creating one
            if(![iStream getBuffer:&buffer length:&length]) {
                // The stream couldn't provide its internal buffer. We have to make one ourselves
                buffer = malloc(BUFFER_LEN * sizeof(uint8_t));
                freeBuffer = YES;
                NSInteger result = [iStream read:buffer maxLength:BUFFER_LEN];
                if(result < 0) {
                    // error copying to buffer
                    break;
                }
                length = result;
            }
            // length bytes of data in buffer
            if(freeBuffer) free(buffer);
            break;
        case NSStreamEventErrorOccurred:
            // some other error
            shouldClose = YES;
            break;
    }
    if(shouldClose) [iStream close];
}