Поток для получения данных - NSInputStream - PullRequest
5 голосов
/ 14 июля 2011

All

У меня есть сервер с потоком сокета tcp для связи. Мне нужно попасть в этот поток и прочитать исходные данные, которые мне нужно отправить.

Мой текущий код выглядит следующим образом. Если честно, я иду на это совершенно слепо. Я не уверен, что это правильно, не говоря уже о том, что нужно для работы.

-(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];

}

Итак, с моей точки зрения, этот код инициализирует поток, но как мне прочитать из потока?

Спасибо

Ответы [ 2 ]

23 голосов
/ 14 июля 2011

Есть два способа получить данные из потока: опрос и использование событий потока.

Опрос проще, но заблокирует поток, в котором он выполняется. Если вы используете этот метод, вы ненеобходимо выполнить вызовы setDelegate: или scheduleInRunLoop:forMode:.Опрос выполняется путем многократного вызова 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

Использование потоковых событий требует установки делегата и добавления потока в цикл выполнения.Вместо того, чтобы блокировать поток, поток будет отправлять сообщение stream:handleEvent: своему делегату, когда происходят определенные события, в том числе, когда он получает данные.Затем делегат может получить данные из потока.Вот пример stream:handleEvent: метода:

- (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];
}
0 голосов
/ 14 марта 2019

Я придумал это, основываясь на некоторых других ответах.

public enum StreamError: Error {
    case Error(error: Error?, partialData: [UInt8])
}

extension InputStream {

    public func readData(bufferSize: Int = 1024) throws -> Data {
        var buffer = [UInt8](repeating: 0, count: bufferSize)
        var data: [UInt8] = []

        open()

        while true {
            let count = read(&buffer, maxLength: buffer.capacity)

            guard count >= 0 else {
                close()
                throw StreamError.Error(error: streamError, partialData: data)
            }

            guard count != 0 else {
                close()
                return Data(bytes: data)
            }

            data.append(contentsOf: (buffer.prefix(count)))
        }

    }
}
...