Как вы должны справиться с возможностью блокировки NSStream? - PullRequest
4 голосов
/ 06 января 2012

В соответствии с «Apple по сравнению с расписанием цикла выполнения»:

[hasSpace/BytesAvailable] может означать, что есть доступные байты или пробел или , которые единственный способ найтиout - попытаться выполнить операцию чтения или записи (которая может привести к кратковременному блоку).

В документе явно не указано, что hasSpace / BytesAvailable события ведут себя одинаковотолько темным образом, что они имеют «идентичную семантику».

Могу ли я заключить, что запись / чтение streamError или возвращение байтов при чтении / записи меньше ожидаемой суммы может быть вызвано «мгновенным блоком»”?

Если так, должен ли я повторить попытку передачи? Должен ли я использовать какой-либо механизм таймера, чтобы дать возможность блокировке очистить? Это было бы много работы для реализации, поэтому я бы предпочелнет, если это вряд ли поможет.

(В таком случае заманчиво инициировать ограниченный цикл опроса, скажем, цикл while, который делает 10 попыток, но я не знаю, безопасно ли это делать на этом этапе.В то же время, когда поток запланирован в цикле выполнения, и у меня нет возможности проверить его.)

Ответы [ 3 ]

1 голос
/ 12 мая 2012

Это был долгий путь. Вот некоторые замечания по этому вопросу:

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

Вместо этого я настраиваю циклы холостого хода:

- (void) stream:(NSStream *)theStream handleEvent:(NSStreamEvent)eventCode {

    switch (eventCode) 
        // RECEIVING
    case NSStreamEventHasBytesAvailable: {
        if (self.receiveStage == kNothingToReceive)
            return;
        // Get the data from the stream. (This method returns NO if bytesRead < 1.)
        if (![self receiveDataViaStream:(NSInputStream *)theStream]) {
            // If nothing was actually read, consider the stream to be idling.
            self.bStreamIn_isIdling = YES;
            // Repeatedly retry read, until (1) the read is successful, or (2) stopNetwork is called, which will clear the idler.
            // (Just in case, add nil stream property as a loop breaker.) 
            while (self.bStreamIn_isIdling && self.streamIn) {
                if ([self receiveDataViaStream:(NSInputStream *)theStream]) {
                    self.bStreamIn_isIdling = NO;
                    // The stream will have started up again; prepare for next event call.
                    [self assessTransmissionStage_uponReadSuccess];
                }
            }
        }
        else
            // Prepare for what happens next.
            [self assessTransmissionStage_uponReadSuccess];
        break;
        // SENDING
    case NSStreamEventHasSpaceAvailable: 
        if (self.sendStage == kNothingToSend)
            return;
        if (![self sendDataViaStream:(NSOutputStream *)theStream]) {
            self.bStreamOut_isIdling = YES;
            while (self.bStreamOut_isIdling && self.streamOut) {
                if ([self sendDataViaStream:(NSOutputStream *)theStream]) {
                    self.bStreamOut_isIdling = NO;
                    [self assessTransmissionStage_uponWriteSuccess];
                }
            }
        }
        else
            [self assessTransmissionStage_uponWriteSuccess]; 
        break;
    // other event cases…

Затем пришло время проверить отмену, инициированную пользователем, с помощью кнопки «отмена». Посередине синхронизации на стороне какао есть пауза, ожидающая ввода данных пользователем. Если пользователь отменяет в этот момент, приложение Какао закрывает потоки и удаляет их из цикла выполнения, поэтому я ожидал, что потоки на стороне другой соединения будут генерировать события NSStreamEventEndEncountered или, возможно, NSStreamEventErrorOccurred. Но нет, только одно событие произошло, NSStreamEventHasBytesAvailable! Пойди разберись.

Конечно, на самом деле не было никаких «доступных байтов», поскольку поток был закрыт на стороне Какао, а не записан - таким образом, обработчик потока на стороне iOS вошел в бесконечный цикл. Не очень хорошо.

Затем я проверил, что произойдет, если одно из устройств перейдет в спящий режим. Во время паузы для пользовательского ввода я оставил iPhone в спящем режиме с помощью автоматической блокировки *, а затем предоставил пользовательский ввод на стороне какао. Снова сюрприз: приложение Cocoa продолжалось без помех до конца синхронизации, и когда я разбудил iPhone, приложение iOS оказалось также завершило свою сторону синхронизации.

Может быть, сбой на стороне iPhone был исправлен моей бездействующей петлей? Я добавил процедуру остановки сети, чтобы проверить:

if (![self receiveDataViaStream:(NSInputStream *)theStream])
    [self stopNetwork]; // closes the streams, etc.

Синхронизация по-прежнему завершена. Икоты не было.

Наконец, я проверил, что произошло, если Mac (сторона какао) во время этой паузы засыпал для ввода. Это привело к некоторой обратной отрыжке: было получено два события NSStreamEventErrorOccurred - на стороне Mac , после чего больше не было возможности записи в выходной поток. Никаких событий вообще не было получено на стороне iPhone, но если я проверил состояние потока iPhone, он вернул бы 5, NSStreamStatusAtEnd.

ВЫВОДЫ И ПЛАН:

  • «Временный блок» - это что-то вроде единорога. Либо сеть работает без сбоев, либо вообще отключается.
  • Если действительно существует такая вещь, как временный блок, нет способа отличить его от полного отключения. Единственными константами состояния потока, которые кажутся логичными для временного блока, являются NSStreamStatusAtEnd и NSStreamStatusError. Но согласно приведенным выше экспериментам, они указывают на отключение.
  • В результате чего я сбрасываю циклы while и выявляю отключение исключительно, проверяя bytesRead / Written <1. </li>

* iPhone никогда не будет спать, если он подчинен Xcode. Вы должны запустить его прямо с iPhone.

1 голос
/ 26 февраля 2018

Вы можете ожидать «разъединение» всякий раз, когда вы пытаетесь записать 0 байтов в выходной поток или когда вы получаете 0 байтов во входном потоке.Если вы хотите сохранить потоки живыми, обязательно проверьте длину байтов, которые вы записываете в выходной поток.Таким образом, входной поток никогда не получает 0 байтов, что запускает обработчик событий для закрытых потоков.

Нет такой вещи, как «выходной поток» в режиме ожидания.Только поставщик холостого хода байтов к выходному потоку, который не должен указывать его бездействие.

Если вы отключаетесь от сетевого подключения с помощью таймера отключения, вы можете отключить его, открываяпотоков, а затем отключите его, когда закроете их:

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
    switch(eventCode) {
        case NSStreamEventOpenCompleted:
        {
            [UIApplication sharedApplication].idleTimerDisabled = YES;
            break;
        }

        case NSStreamEventEndEncountered:
        {
            [UIApplication sharedApplication].idleTimerDisabled = NO;
            break;
        }
    }
}

Я бы не стал углубляться в особенности вашей ситуации, потому что сразу могу сказать, что вы не совсемясно, что такое потоки, точно.Я понимаю, что документация по потокам действительно плоха для подготовки новичков и скудна для загрузки;но они моделируют те же потоки, которые существовали в течение 30 лет, поэтому любая документация по потокам для любой операционной системы (кроме Windows) прекрасно подойдет для ускорения.

Кстати, другиеНеотъемлемой частью потоков является код вашего сетевого подключения, который вы не указали.Я полагаю, что, если вы еще не используете NSNetService и NSNetServiceBrowser для поиска пиров, подключения к ним и получения ваших потоков соответственно, вам следует.Это позволяет вам легко отслеживать состояние сетевого подключения и быстро и легко открывать ваши потоки, если они неожиданно закрылись.

У меня есть очень подробный, но простой для понимания пример кода для этого, который быне требует никаких настроек на вашем конце для использования, если кому-то это понравится.

1 голос
/ 06 января 2012

Вот хорошая оболочка для сокетов: https://github.com/robbiehanson/CocoaAsyncSocket

Она будет читать и записывать в очередь, если соединение недоступно.Вы не упоминаете, используете ли вы UDP или TCP, однако я подозреваю, что вы используете TCP, и в этом случае он будет обрабатывать любые прерывания самостоятельно - при условии, что соединение не будет разорвано.

...