помогите с async_read_until

У меня возникли проблемы с реализацией третьего параметра в функции, описанной здесь: http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/reference/async_read_until/overload4.html Что я хотел бы уметь делать использует обратный вызов для третьего параметра async_read_until, чтобы определить, когда прибыл полный фрагмент. Мои пакеты имеют следующий формат.

  • 1 байт идентификатор (семантическое значение данных)
  • unsigned int (количество байтов в данных, так как некоторые фрагменты данных могут изменять размер)
  • полезная нагрузка

Глядя на пример кода в документации, я немного запутался в том, как я должен извлекать байт, не говоря уже о беззнаковом целом из начального и конечного итераторов. Я создал свои итераторы как typedef boost::asio::buffers_iterator< boost::asio::streambuf::const_buffers_type> iterator;

но даже тогда я не уверен, что это за тип, так как я не знаю, что такое const_buffers_type. Я перешел по некоторым ссылкам в документации и обнаружил, что это «определена реализацией», но я думаю, что могу ошибаться. Итак, два моих конкретных вопроса:

  1. как я могу использовать эти два итератора для чтения беззнакового целого числа?
  2. на какой тип указывают эти итераторы?

Спасибо!


person Chris H    schedule 02.01.2010    source источник
comment
Можете ли вы четко указать, как вы определяете конец пакета?   -  person Kornel Kisielewicz    schedule 03.01.2010


Ответы (2)


Пример функции сопоставления представлен в документации.

std::pair<iterator, bool>
match_whitespace(iterator begin, iterator end)
{
  iterator i = begin;
  while (i != end)
    if (std::isspace(*i++))
      return std::make_pair(i, true);
  return std::make_pair(i, false);
}

Разыменование i здесь извлекает один байт. Вам нужно вытащить достаточно байтов, чтобы соответствовать int.

Однако помните, что обратный вызов — не единственный вариант для read_until. На самом деле он самый сложный. Вы уверены, что недостаточно использовать регулярное выражение?

template<
    typename AsyncReadStream,
    typename Allocator,
    typename ReadHandler>
void async_read_until(
    AsyncReadStream & s,
    boost::asio::basic_streambuf< Allocator > & b,
    const boost::regex & expr,
    ReadHandler handler);

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

person Kornel Kisielewicz    schedule 03.01.2010

У меня формат сообщения очень похож на ваш (16-битная длина полезной нагрузки, 8-битный идентификатор/тип пакета, за которым следует полезная нагрузка для меня). Я сделал это с трехфазным чтением и массивом указателей на функции для обработки разных вещей. Я использовал boost::asio::async_read для чтения известных сумм за раз.

Это упрощенная версия моего кода:

//call this to start reading a packet/message
void startRead(boost::asio::ip::tcp::socket &socket)
{
    boost::uint8_t *header = new boost::uint8_t[3];
    boost::asio::async_read(socket,boost::asio::buffer(header,3),
        boost::bind(&handleReadHeader,&socket,header, 
        boost::asio::placeholders::bytes_transferred,boost::asio::placeholders::error));
}
void handleReadHeader(boost::asio::ip::tcp::socket *socket,
    boost::uint8_t *header, size_t len, const boost::system::error_code& error)
{
    if(error)
    {
        delete[] header;
        handleReadError(error);
    }
    else
    {
        assert(len == 3);
        boost::uint16_t payLoadLen  = *((boost::uint16_t*)(header + 0));
        boost::uint8_t  type        = *((boost::uint8_t*) (header + 2));
        delete[] header;
        //dont bother calling asio again if there is no payload
        if(payLoadLen > 0)
        {
            boost::uint8_t *payLoad = new boost::uint8_t[payLoadLen];

            boost::asio::async_read(*socket,boost::asio::buffer(payLoad,payLoadLen),
                boost::bind(&handleReadBody,socket,
                type,payLoad,payLoadLen,
                boost::asio::placeholders::bytes_transferred,boost::asio::placeholders::error));
        }
        else handleReadBody(socket,type,0,0,0,boost::system::error_code());
    }
}
void handleReadBody(ip::tcp::socket *socket,
    boost::uint8_t type, boost::uint8_t *payLoad, boost::uint16_t len,
    size_t readLen, const boost::system::error_code& error)
{
    if(error)
    {
        delete[] payLoad;
        handleReadError(error);
    }
    else
    {
        assert(len == readLen);
        //passes the packet to the appropriate function for the type
        //you could also use a switch statement or whatever
        //to get the next packet you must call StartRead again
        //personally I choose to do this from the actaul handler
        //themselves
        handlePacket(type,payLoad,len,error);
    }
}
person Fire Lancer    schedule 03.01.2010