Осуществляю чтение по 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?
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
Здравствуйте, 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 тупо константа на максимальный размер буфера)? Если "так надо", то для общего случая нужно организовывать собственный цикл дочитывания или я что-то упустил?
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
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
Здравствуйте, 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:
Когда вызывается конструктор mutable_buffers_1 там явно проверяется размер буфера и если он > buffer_max_length, то размер созданного буфера ограничивается этой константой (65535). Т.е. я прошу 90000, а transfer_all ожидает 65535. Вот собственно я и интересуюсь или я что-то не понял или это так нужно.
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