如何在async_read_until之后使用asio缓冲区进行连续读取

时间:2017-06-11 21:51:30

标签: c++ serial-port boost-asio

我正在从串行设备读取,其中必须特别请求每条消息。例如。您发送请求并获得序列化有效负载的响应。

每封邮件按顺序包含以下部分:

  1. PREAMBLE(2个字节,“$ M”)
  2. HEADER(3个字节,包含有效负载长度N)
  3. PAYLOAD + CRC(N + 1个字节)
  4. 我使用asio的方法是使用asio::async_read_until检测消息的开始(PREAMBLE),然后使用asio::async_read来读取HEADER和PAYLOAD + CRC的确切字节数。由于邮件末尾没有静态模式,因此我无法使用async_read_until来阅读完整邮件。

    收到PREAMBLE后,async_read_until的处理程序被调用,缓冲区包含PREAMBLE字节,可能包含来自HEADER和PAYLOAD + CRC的附加字节。 async_read_until的asio文档说:

      

    成功执行async_read_until操作后,streambuf可以   包含分隔符之外的其他数据。申请将   通常将该数据留在streambuf中以供后续使用   要检查的async_read_until操作。

    我将此解释为您应该只使用请求的字节并将所有剩余的字节留在缓冲区中以供进一步读取。 但是,所有连续读取都会阻止,因为数据已经在缓冲区中,并且设备上没有任何内容。

    读取实现为小型状态机processState,其中根据要读取消息的哪个部分来注册不同的处理程序。所有阅读都使用相同的bufferasio::streambuf)完成。 processState在无限循环中被调用。

    void processState() {
        // register handler for incomming messages
        std::cout << "state: " << parser_state << std::endl;
        switch (parser_state) {
        case READ_PREAMBLE:
            asio::async_read_until(port, buffer, "$M",
                std::bind(&Client::onPreamble, this, std::placeholders::_1, std::placeholders::_2));
            break;
        case READ_HEADER:
            asio::async_read(port, buffer, asio::transfer_exactly(3),
                std::bind(&Client::onHeader, this, std::placeholders::_1, std::placeholders::_2));
            break;
        case READ_PAYLOAD_CRC:
            asio::async_read(port, buffer, asio::transfer_exactly(request_received->length+1),
                std::bind(&Client::onDataCRC, this, std::placeholders::_1, std::placeholders::_2));
            break;
        case PROCESS_PAYLOAD:
            onProcessMessage();
            break;
        case END:
            parser_state = READ_PREAMBLE;
            break;
        }
        // wait for incoming data
        io.run();
        io.reset();
    }
    

    接收PREAMBLE时调用PREAMBLE处理程序onPreamble

    void onPreamble(const asio::error_code& error, const std::size_t bytes_transferred) {
        std::cout << "onPreamble START" << std::endl;
        if(error) { return; }
    
        std::cout << "buffer: " << buffer.in_avail() << "/" << buffer.size() << std::endl;
    
        // ignore and remove header bytes
        buffer.consume(bytes_transferred);
    
        std::cout << "buffer: " << buffer.in_avail() << "/" << buffer.size() << std::endl;
    
        buffer.commit(buffer.size());
    
        std::cout << "onPreamble END" << std::endl;
        parser_state = READ_HEADER;
    }
    

    在此处理程序之后,没有其他处理程序被调用,因为数据在缓冲区中并且设备上没有数据。

    使用asio::streambuf的正确方法是什么,以便连续async_read的处理程序被调用,我可以按状态机的顺序处理字节?我不知道我想处理onPreamble中的剩余字节,因为不能保证这些字节包含完整的消息。

1 个答案:

答案 0 :(得分:1)

您无需在buffer.commit()处理程序中调用onPreamble()。调用buffer.consume()将按预期删除标头字节,并在asio::streambuf中保留剩余字节(如果收到任何字节)以供下次读取。当您填充数据以发送给远程方时,将使用streambuf的prepare()commit()调用。

我刚刚完成了blog post and codecast关于使用asio :: streambuf与一些Web服务器执行简单HTTP GET的问题。它可能会让您更好地了解如何使用async_read_until()async_read()