为什么提升asio async_read_some在某种情况下不会调用回调?

时间:2015-05-21 07:52:55

标签: c++ sockets boost boost-asio

我正在实现一种串行通信协议,通过UART与外部设备进行通信。我正在使用boost asio。到目前为止一切正常,除了很少的情况。我发现在某些情况下,套接字的读取可能“miss” 1个字节。

我使用UART嗅探器查看了串行通信,以查看来自套接字的原始数据。这很难过,我确实看到通过套接字传输的完整字节帧。但在我的应用程序中,最后一个字节没有收到。

我发现他的字节不是“丢失”它在某种程度上只是“卡住”在内核缓冲区或其他什么东西。因为在接收到下一帧时,我会在所有新字节前面得到“missing”字节。如果框架连续进入,这不会是一个大问题。但是,如果程序确认了前一帧,则下一帧仅由设备发送。

这是我的写和处理接收方法的代码:

void serial::write(std::vector<uint8_t> message) {
    static boost::mutex mu;
    mu.lock();
    uint8_t cmd = message.at(3);
    //if its an ACK frame - just send it
    if ((message[3]>>4)==0xE) {
        // --- Write message to serial port --- //
        boost::asio::write(serial_,boost::asio::buffer(message));
        usleep(20000);
        mu.unlock();
        return;
    }
    //if its not an ACK frame write the frame and wait for an ACK!
    //create new promise for the request
    promise = new boost::promise<deque<uint8_t>>;
    boost::unique_future<deque<uint8_t>> future = promise->get_future();
    // --- Write message to serial port --- //
    boost::asio::write(serial_,boost::asio::buffer(message));
    usleep(20000);
    mu.unlock();
    //wait for ACK or timeout
    if     (future.wait_for(boost::chrono::milliseconds(100))==boost::future_status::timeout) {
        spdlog::get("logger")->trace("ACK timeout!");
        //delete pointer and set it to 0
        delete promise;
        promise=nullptr;
        //need to resend this frame
    }
    //delete pointer and set it to 0 after getting a message
    delete promise;
    promise=nullptr;
}

处理接收:

void serial::handle_receive(const boost::system::error_code& error, size_t bytes_transferred) {
     static deque<uint8_t> read_buffer;
     static boost::posix_time::ptime start =    boost::posix_time::microsec_clock::local_time( );
    if (boost::posix_time::microsec_clock::local_time( ) - start > boost::posix_time::milliseconds(99)  && !read_buffer.empty()) {
        spdlog::get("logger")->trace("time condition, clearing buffer!");
        read_buffer.clear();
    }
    start = boost::posix_time::microsec_clock::local_time( );
    if (!error) {
        //push all the recieved data into the deque
        for (unsigned int i = 0; i < bytes_transferred; i++) {
            read_buffer.push_back((int)data_[i]);
        }
    }
    else if (error){
    spdlog::get("logger")->error("\n\n\n\nERROR: {0}\n\n\n\n", error);
    }
    while ( !read_buffer.empty()) {
        /*do here some checks if this could be a correct frame
       ...
       ...
       ...
       */
        //received frame is ready
        //check if it is an ACK frame
        //check if its a data frame and ACK it!
        //send an ACK if its a dataframe
        if (ACK)) {
            write(frame);
        }
        //give the data to the upper layer                  
        m_Receive_data_handler(receivedFrame,receivedFrame.size());
        }
    serial_.async_read_some(boost::asio::buffer(data_,max_length),
                        boost::bind(&serial::handle_receive, this,
                                    boost::asio::placeholders::error,
                                    boost::asio::placeholders::bytes_transferred));

}

也许有人知道为什么它适用于大多数情况,但只是在帧的最后一个字节失败?

谢谢!

2 个答案:

答案 0 :(得分:0)

您正在使用异步调用来读取数据,并且无法保证您将阅读所有内容。

如果您查看文档: http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/reference/basic_stream_socket/async_read_some.html

  

说明

     

读取操作可能无法读取所有请求的字节数。   如果需要确保使用async_read函数,请考虑使用   在异步操作之前读取请求的数据量   完成。

可能会发生的情况是,在您的情况下,您的内核具有很少的缓冲区,并且当它有一些数据时它会向应用程序发出信号,但是您尝试发送的数据多于此数据。一旦它只停留在缓冲区中的小数据,它将等待更多的数据来保存呼叫。这些只是假设,我没有UART的第一手经验。

是否可以选择切换到同步读取调用(read_some)?

答案 1 :(得分:0)

可能是这样,而不是卡在某个内核缓冲区中,最后一个字节是否卡在UART的接收FIFO中?也许您需要使用字符间超时配置驱动程序?