Как читать данные переменной длины из асинхронного TCP-сокета?

Я использую CocoaAsyncSocket для проекта iOS. Я пытаюсь читать VarInts через асинхронный интерфейс. Проблема в отличие от чего-то еще, например String, где я могу поставить префикс длины, я не знаю длины varint заранее. Он должен обрабатываться по одному байту за раз, но поскольку каждая операция чтения является асинхронной, другие вызовы чтения могли быть поставлены в очередь между ними.

Я подумал о том, чтобы прочитать в буфер, затем обработать его, скажем, прочитать 5 байтов (максимальная длина для varint-32) и отодвинуть дополнительные байты назад, но это может излишне зависать, если varint составляет всего 4 байта, и я жду 5-й байт будет доступен.

Как я могу это сделать? Кроме того, я не могу изменить протокол на другом конце, чтобы использовать фиксированные размеры.


Вот фрагмент кода, который попросил Джош

- (void)readByte:(void (^)(int8_t))onComplete {
    NSUInteger size = 1;
    int32_t tag = OSAtomicAdd32(1, &_nextTag);
    dispatch_async(self.dispatchQueue, ^{
        [self.onCompleteHandlers setObject:(^void (NSData* data) {
            int8_t x = 0;
            [data getBytes:&x length:size];
            onComplete(x);
        }) forKey:[NSNumber numberWithInteger:((NSInteger) tag)]];
        [self.socket readDataToLength:size withTimeout:-1 tag:tag];
    });
}

Обратный вызов сохраняется в словаре, который используется в методе делегата socket: didReadData: withTag,

Предположим, я читаю байт VarInt:

  • выполнить чтение первого байта для varint
  • не знаю, нужно ли нам читать другой байт для varint или нет; это зависит от результата первого чтения
  • (возможно) прочитать другой байт для чего-то другого
  • прочитайте второй байт для varint, но теперь это фактически третий читаемый байт

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

1 ответ

Короче есть 4 способа узнать сколько читать из сокета...

  1. прочитайте некоторый формат, который вы можете определить длину, как Content-Length header... работает только в том случае, если весь запрос может быть составлен перед отправкой тела.
  2. читать до некоторой картины: как \r\n\r\n в конце заголовков
  3. читать до некоторого времени ожидания... после того, как вы не получите байтов через n секунд, вы очищаете буферы и закрываете соединение.
  4. читать до тех пор, пока сервер не закроет соединение... на самом деле довольно часто.

у каждого из них есть проблемы, и я бы, вероятно, отказался от использования какого-либо существующего протокола.

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

client>"doMath(2+5)\0"

server>"(7)\0"

но трудно ответить на ваш общий вопрос конкретно.

редактировать:

Так что я немного подробнее рассмотрел проблему с varint base-128 и думаю, что на самом деле будет работать только тайм-аут или сервер, закрывающий соединение, если вы пишете их прямо на уровне TCP, что ужасно...

Другие вопросы по тегам