iphoneobjective-cxcodensstream

Stream to Get Data - NSInputStream


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


Solution

  • 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];
    }