Чтение неизвестного количества входящих байтов

Мое приложение взаимодействует с сервером через TCP, используя AsyncSocket. Возможны две ситуации, в которых происходит общение:

  1. Приложение что-то отправляет на сервер, сервер отвечает. Приложению необходимо прочитать этот ответ и что-то сделать с содержащейся в нем информацией. Этот ответ всегда имеет одинаковую длину, например, ответ всегда 6 байт.

  2. Приложение «бездействует», и сервер в какой-то момент (неизвестный приложению) инициирует связь. Приложение должно прочитать все, что отправляет сервер (может быть любое количество байтов, но первый байт будет указывать, сколько байтов следует, чтобы я знал, когда прекратить чтение) и обработать эту информацию.

Первая ситуация работает нормально. readDataToLength:timeout:tag возвращает то, что мне нужно, и я могу делать с этим то, что хочу. Это вторая ситуация, которую я не знаю, как реализовать. Я не могу использовать readDataToLength:timeout:tag, так как заранее не знаю длину.

Я думаю, что мог бы что-то сделать с readDataWithTimeout:tag:, установив тайм-аут на -1. Я полагаю, что это заставляет сокет постоянно прослушивать все, что приходит. Однако это, вероятно, помешает данным, поступающим в ответ на то, что я отправил (ситуация 1). Приложение больше не может отличать входящие данные от ситуации 1 или ситуации 2.

Кто-нибудь здесь, кто может помочь мне решить эту проблему?


person Scott Berrevoets    schedule 16.11.2012    source источник


Ответы (1)


Ваша ошибка в дизайне сетевого протокола.

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

Один простой способ исправить протокол в вашем случае (если инициированные сервером сообщения всегда меньше 255 байт) - добавить 7-й байт в начало ответа, со значением FF.

Таким образом вы можете прочитатьDataWithTimeout:tag: для 1 байта. По тайм-ауту вы повторяете попытку, пока не появятся данные. Если полученное значение равно FF, вы читаете еще 6 байт с помощью readDataToLength:6 timeout: tag: и интерпретируете это как ответ на отправленный ранее запрос. Если это другое значение, вы читаете сообщение с помощью readDataToLength:theValue timeout: tag: и обрабатываете сообщение, инициированное сервером.

person Soonts    schedule 16.11.2012
comment
Текущий протокол на самом деле имеет что-то очень похожее на ваше решение. Хотя это не совсем то же самое, я думаю, что теперь знаю, как это реализовать. Огромное спасибо! - person Scott Berrevoets; 17.11.2012
comment
Похоже, что после того, как я вызвал начальный readDataToLength (только для 1 байта, чтобы увидеть, сколько еще байтов мне нужно прочитать), дальнейшие вызовы readDataToLength игнорируются. Если я вызову [socket readDataToLength:bytesExpected withTimeout:-1 tag:0], я все равно буду получать только 1 байт за раз. (О, я полагаю, вы имеете в виду readDataToLength, а не readDataWithTimeout?) - person Scott Berrevoets; 17.11.2012
comment
Вам необходимо выполнить следующие действия в указанном порядке: 1. Выполните 1-байтовый запрос на чтение для чтения заголовка. 2. Дождитесь данных. 3. Выполните запрос на чтение n байтов. 4. Дождитесь данных, получите буфер данных. У меня такое чувство, что вы либо забыли шаг № 2, либо ваш код пытается параллельно читать и ответы, и сообщения, инициированные сервером (вам определенно нужно сериализовать доступ для чтения к сокету). - person Soonts; 17.11.2012
comment
Не поверите, я именно так и делаю. Я использую теги, чтобы отличить заголовок от данных, и кажется, что все это читается с помощью тега заголовка. Я также не единственный с этой проблемой: stackoverflow.com/questions/6784872/. Хотя я не использую Erlang, у меня та же проблема. - person Scott Berrevoets; 17.11.2012
comment
Проблема решена, был еще один вызов readDataToLength, который все испортил. В очередной раз благодарим за помощь! - person Scott Berrevoets; 17.11.2012
comment
Пожалуйста. Да, при разработке асинхронного кода легко начать делать параллельно то, что должно быть последовательно. - person Soonts; 17.11.2012