boost async_read && transfer_at_least
От: tris  
Дата: 14.08.12 15:08
Оценка:
Просветите, пожалуйста.

Осуществляю чтение по TCP или Serial линии. Реализуется блокирующее чтение с помощью async_read + deadline_timer + run_one в цикле.
По протоколу (ModBus RTU/TCP) чтение может вернуть 2 легальных ответа: или запрошенное количество байт (max_size), или сообщение с ошибкой (min_size).
Чтобы обработать эту ситуацию async_read вызывается с transfer_at_least:


std::vector<uchar> buff(max_size); 
// max_size - это заранее известное значение, 
//т.е. послав запрос я знаю сколько должно быть в случае нормального ответа

...skip...

boost::asio::async_read(io_object_, boost::asio::buffer(buffer, max_size),
        boost::asio::transfer_at_least(min_size),
        boost::bind(&CommonMedium::operation_completed, this,
                    &error, &transfered, ph::error, ph::bytes_transferred));

// т.е. мне нужно либо min_size байт, либо max_size


Собственно вопросы:

1. Правильно ли я понимаю, что если используется transfer_at_least, то фактически async_read превращается в async_read_some, т.е. мне нужно самому заботиться о дочитывании в ситуации, когда реально прочиталось > min_size, но < max_size? Т.е. данные фрагментировались.
2. При каких размерах пакетов (Serial/TCP) может возникнуть ситуация из п.1?
Re: boost async_read && transfer_at_least
От: abrarov Россия http://asio-samples.blogspot.com/
Дата: 14.08.12 21:09
Оценка: 2 (1)
T>1. Правильно ли я понимаю, что если используется transfer_at_least, то фактически async_read превращается в async_read_some, т.е. мне нужно самому заботиться о дочитывании в ситуации, когда реально прочиталось > min_size, но < max_size? Т.е. данные фрагментировались.

Да, но только в общем случае. Читаем документацию:

completion_condition
The function object to be called to determine whether the read operation is complete. The signature of the function object must be:

std::size_t completion_condition(
  // Result of latest async_read_some operation.
  const boost::system::error_code& error,

  // Number of bytes transferred so far.
  std::size_t bytes_transferred
);

A return value of 0 indicates that the read operation is complete. A non-zero return value indicates the maximum number of bytes to be read on the next call to the stream's async_read_some function.


Т.е. если Вы сможете написать completion_condition так, чтобы он уже после приема min_size байт (это максимальное значение предела) мог точно сказать, сколько еще байт необходимо прочитать (0 или max_size — min_size), то async_read считает ровно то кол-во байт, что Вам нужно.
Programs must be written for people to read, and only incidentally for machines to execute
Re[2]: boost async_read && transfer_at_least
От: tris  
Дата: 14.08.12 22:33
Оценка:
Здравствуйте, abrarov, Вы писали:

A>Т.е. если Вы сможете написать completion_condition так, чтобы он уже после приема min_size байт (это максимальное значение предела) мог точно сказать, сколько еще байт необходимо прочитать (0 или max_size — min_size), то async_read считает ровно то кол-во байт, что Вам нужно.


Благодарю за пояснение. Да, я прочитал про completion_condition, но вот смущает именно "мог точно сказать, сколько еще байт необходимо прочитать". Интересует общий подход к подобным случаям (протокол, у которого есть легальные пакеты минимальной длины — min_size и "штатные" — max_size). Вот случаи, с которыми у меня проблемы:
1. Фрагментация пакета происходит строго по min_size, т.е. если бы пришло min_size+1, то можно было бы понять, что это не минимальный ответ, а полный. Как себя поведет socket_base::bytes_readable в completion_condition в данном случае?
2. И совсем глупый вопрос..Я попытался прочитать по Serial 1 Mb данных за один вызов функции чтения (использовал синхронное чтение read). В итоге прочиталось 65535 байт. С чем связано данное ограничение (в кишках asio тупо константа на максимальный размер буфера)? Если "так надо", то для общего случая нужно организовывать собственный цикл дочитывания или я что-то упустил?
Re[3]: boost async_read && transfer_at_least
От: abrarov Россия http://asio-samples.blogspot.com/
Дата: 15.08.12 05:51
Оценка:
T>1. Фрагментация пакета происходит строго по min_size, т.е. если бы пришло min_size+1, то можно было бы понять, что это не минимальный ответ, а полный. Как себя поведет socket_base::bytes_readable в completion_condition в данном случае?

Вы же как-то будете определять тип пакета (не по размеру, конечно, потому что TCP — потоковый протокол)? Таким образом, в completion_condition в худшем случае при получении min_size Вы уже точно сможете сказать каким типом пакета (не)является полученная последовательность байт.

T>2. И совсем глупый вопрос..Я попытался прочитать по Serial 1 Mb данных за один вызов функции чтения (использовал синхронное чтение read). В итоге прочиталось 65535 байт. С чем связано данное ограничение (в кишках asio тупо константа на максимальный размер буфера)? Если "так надо", то для общего случая нужно организовывать собственный цикл дочитывания или я что-то упустил?


Такого ограничения внутри Asio нет (есть, кажется, ограничение на кол-во буферов в BufferSequence). То, что Вы отправляете 1Мб, вовсе не означает, что принимающая сторона получит этот мегабайт за одну операцию чтения. Если Вам это не знакомо (учитывая Ваше "что-то упустил"), то рекомендую "Эффективное программирование TCP/IP", Йон Снейдер.
Programs must be written for people to read, and only incidentally for machines to execute
Re[4]: boost async_read && transfer_at_least
От: abrarov Россия http://asio-samples.blogspot.com/
Дата: 15.08.12 05:55
Оценка:
T>>2. И совсем глупый вопрос..Я попытался прочитать по Serial 1 Mb данных за один вызов функции чтения (использовал синхронное чтение read). В итоге прочиталось 65535 байт. С чем связано данное ограничение (в кишках asio тупо константа на максимальный размер буфера)? Если "так надо", то для общего случая нужно организовывать собственный цикл дочитывания или я что-то упустил?

A>Такого ограничения внутри Asio нет (есть, кажется, ограничение на кол-во буферов в BufferSequence). То, что Вы отправляете 1Мб, вовсе не означает, что принимающая сторона получит этот мегабайт за одну операцию чтения. Если Вам это не знакомо (учитывая Ваше "что-то упустил"), то рекомендую "Эффективное программирование TCP/IP", Йон Снейдер.


Извините, упустил, что это чтение по Serial. В чем здесь дело — не знаю. Полагаю, надо смотреть документацию ОС по чтению из serial.
Programs must be written for people to read, and only incidentally for machines to execute
Re[4]: boost async_read && transfer_at_least
От: tris  
Дата: 15.08.12 15:49
Оценка:
Здравствуйте, abrarov, Вы писали:

A>Вы же как-то будете определять тип пакета (не по размеру, конечно, потому что TCP — потоковый протокол)? Таким образом, в completion_condition в худшем случае при получении min_size Вы уже точно сможете сказать каким типом пакета (не)является полученная последовательность байт.


Да, Вы абсолютно правы, я как-то упустил "TCP — потоковый протокол" в погоне за сферической общностью . completion_condition полностью покрывает то, что необходимо.

A>Такого ограничения внутри Asio нет (есть, кажется, ограничение на кол-во буферов в BufferSequence). То, что Вы отправляете 1Мб, вовсе не означает, что принимающая сторона получит этот мегабайт за одну операцию чтения. Если Вам это не знакомо (учитывая Ваше "что-то упустил"), то рекомендую "Эффективное программирование TCP/IP", Йон Снейдер.


Под "что-то упустил" я имел в виду что-то в работе/документации asio. Да, за одну операцию (read_some в терминах asio) я 1Мб могу не получить. Но я использую free метод read или async_read, которые, как я полагаю, делают работу за меня. Т.е. вызов read или async_read последовательно вызывают read_some/async_reaad_some до тех пор, пока мой буфер не будет заполнен или не произойдет ошибка (completion_condition — transfer_all, по-умолчанию). Интересует меня следующее поведение mutable_buffers:

// read method
std::vector<uchar> read_buff(90000);

boost::asio::async_read(io_object_, boost::asio::buffer(buffer, 90000),
        boost::bind(&CommonMedium::operation_completed, this,
                    &error, &transfered, ph::error, ph::bytes_transferred));


Когда вызывается конструктор mutable_buffers_1 там явно проверяется размер буфера и если он > buffer_max_length, то размер созданного буфера ограничивается этой константой (65535). Т.е. я прошу 90000, а transfer_all ожидает 65535. Вот собственно я и интересуюсь или я что-то не понял или это так нужно.
Re[5]: boost async_read && transfer_at_least
От: abrarov Россия http://asio-samples.blogspot.com/
Дата: 15.08.12 18:37
Оценка:
T>Когда вызывается конструктор mutable_buffers_1 там явно проверяется размер буфера и если он > buffer_max_length, то размер созданного буфера ограничивается этой константой (65535).

Это вопрос? В исходниках Boost.Asio (Boost 1.50, /boost/asio/buffer.hpp) я ничего подобного не нашел.
Programs must be written for people to read, and only incidentally for machines to execute
Re[6]: boost async_read && transfer_at_least
От: tris  
Дата: 15.08.12 21:31
Оценка:
Здравствуйте, abrarov, Вы писали:

A>Это вопрос? В исходниках Boost.Asio (Boost 1.50, /boost/asio/buffer.hpp) я ничего подобного не нашел.


Прошу прощения, это я напутал, когда в дебагере в кишках asio ковырялся. Там в impl/read.hpp:

template <typename AsyncReadStream,
      typename CompletionCondition, typename ReadHandler>
  class read_op<AsyncReadStream, boost::asio::mutable_buffers_1,
      CompletionCondition, ReadHandler>
    : detail::base_from_completion_cond<CompletionCondition>
  {
    void operator()(const boost::system::error_code& ec,
        std::size_t bytes_transferred, int start = 0)
    {
      std::size_t n = 0;
      switch (start)
      {
        case 1:
        n = this->check_for_completion(ec, total_transferred_); // вот тут default_max_transfer_size = 65536 вернулся
        for (;;)
        {
          stream_.async_read_some(boost::asio::buffer(
                buffer_ + total_transferred_, n), *this); // и вот тут я проморгал operator+
          return; default:
          total_transferred_ += bytes_transferred;
          if ((!ec && bytes_transferred == 0)
              || (n = this->check_for_completion(ec, total_transferred_)) == 0
              || total_transferred_ == boost::asio::buffer_size(buffer_))
            break;
        }

        handler_(ec, static_cast<const std::size_t&>(total_transferred_));
      }
    }
  };
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.