Boost ASIO,未调用 async_read_some 回调

Posted

技术标签:

【中文标题】Boost ASIO,未调用 async_read_some 回调【英文标题】:Boost ASIO, async_read_some callback not called 【发布时间】:2016-03-03 22:43:31 【问题描述】:

我的代码适用于read_some,但不适用于async_read_some。我正在读取的数据是 5 个字符长,而 MAX_RESPONSE_SIZE 256.打开端口后,我从我的主服务器上调用了一次async_read_some,但在我刷了几次代理卡后,从未调用过回调。我尝试在async_read_some 之后添加io_service.run(),但没有帮助。我错过了什么吗?谢谢你。

标题

boost::system::error_code error;
boost::asio::io_service io_service;
typedef boost::shared_ptr<boost::asio::serial_port> serial_port_ptr;
serial_port_ptr serial_port;
char read_buffer[MAX_RESPONSE_SIZE];

打开

serial_port.reset();
serial_port = serial_port_ptr(new boost::asio::serial_port(io_service));
serial_port->open(device_path, error);

serial_port->set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
serial_port->set_option(boost::asio::serial_port_base::character_size(8));
serial_port->set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
serial_port->set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
serial_port->set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));

boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));

阅读

serial_port->async_read_some(
            boost::asio::buffer(read_buffer, MAX_RESPONSE_SIZE),
            boost::bind(
                &serial_comm::data_received,
                this, boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred
            )
        );

回调

void serial_comm::data_received(const boost::system::error_code& error, size_t bytes_transferred)

    // do stuff

【问题讨论】:

我们需要查看调用async_read_some的整个代码流,从io_service调度它的地方开始,到它返回的地方结束,允许线程做其他工作。很可能,调用run 的线程正在执行其他操作,因此无法处理完成。 (您可以通过使用调试器停止程序并查看该线程在做什么来检查。) 我调用open,然后从int main读取;没有别的了。 boost线程不运行io_service吗? 您的io_service.run() 可能会立即返回,因为没有工作。通常的做法是创建一个io_service::work 并将其附加到io_service,以防止run() 返回。 @xinthose 可能由于某种原因它不是。使用调试器找出它在做什么。它本可以终止。它可能会卡在等待其他代码中的某些内容。 boost::asio::io_service::work work (io_service); 之前添加boost::thread t(boost::bind(&amp;boost::asio::io_service::run, &amp;io_service)); 也无济于事; read_some 阻塞,直到我刷卡并且有数据,然后它得到它,我需要复制它;或者只是将read_some 放在一个线程中 【参考方案1】:

您必须确保始终有工作要做,以便 io_service::run() 不会返回并完成正在运行的线程。

如 cmets 中所述,您可以创建一个 io_service::work。但是,我认为这是人为的,是设计问题的征兆。

更好的答案可能是在 data_received 处理程序中,如果没有发生致命错误,您应该为下一次读取做好准备

void serial_comm::data_received(
  const boost::system::error_code& error,
  size_t bytes_transferred)

    // do stuff

   if( any_kind_of_fatal_error )
   
       // return without setting up the next read
       // this will end reading
      return;
   

   // the last read was successful
   // so setup for the next
   serial_port->async_read_some(
        boost::asio::buffer(read_buffer, MAX_RESPONSE_SIZE),
        boost::bind(
            &serial_comm::data_received,
            this, boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred
        )
    );


【讨论】:

谢谢,但永远不会调用data_received;我想我需要在async_read_some 之后启动 io_service 运行线程才能正常工作? 是的。请注意,data_received 将在执行 run() 的线程中执行。因此,为了清楚起见,我也会从该线程执行 async_read_some。【参考方案2】:

基本上我的问题是在同一函数中async_read_some 之后没有启动io_service 线程。你能怪我吗?这东西不是很清楚。这是我的代码,以防有人需要(INFO 和 ERROR 来自 boost 日志记录,请参阅我的其他问题之一):

serial_comm.hpp

#ifndef __SERIAL_COMM_HPP
#define __SERIAL_COMM_HPP

#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>
#include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>

#include <string>
#include <atomic>

#include "logging.hpp"  // Boost logging

#define MAX_RESPONSE_SIZE 256

class serial_comm

    public:
        void open_serial_port (std::string device_path, unsigned int baud_rate);
        void close_serial_port (void);
        void async_read_some (void);

        std::string serial_read_data;
        std::atomic <bool> serial_data_read_completefalse;
    private:
        // functions
        void data_received (const boost::system::error_code& ec, size_t bytes_transferred);

        // variables
        boost::mutex mutex;
        boost::system::error_code error;
        boost::asio::io_service io_service;
        typedef boost::shared_ptr<boost::asio::serial_port> serial_port_ptr;
        serial_port_ptr serial_port;
        char read_buffer[MAX_RESPONSE_SIZE];
;

#endif  // __SERIAL_COMM_HPP

serial_comm.cpp

#include "../include/serial_comm.hpp"

void serial_comm::open_serial_port (std::string device_path, unsigned int baud_rate)
   
    INFO << "started";

    try
    
        serial_port.reset();
        serial_port = serial_port_ptr(new boost::asio::serial_port(io_service));
        serial_port->open(device_path, error);
        if (error) 
        
            ERROR << "error.message() >> " << error.message().c_str();
            throw -3;
        
        // set options
        serial_port->set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
        serial_port->set_option(boost::asio::serial_port_base::character_size(8));
        serial_port->set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
        serial_port->set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
        serial_port->set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));    

    
    catch (int error)
    
        ERROR << "error = " << error;
        throw -1;
    
    catch (const std::exception &e)
    
        ERROR << "e.what() = " << e.what();
        throw -2;
    

    INFO << device_path << " opened correctly";     
    INFO << "ended";

    return;


void serial_comm::close_serial_port()

    boost::mutex::scoped_lock lock(mutex);  // prevent multiple thread access

    INFO << "started";

    try
    
        if (serial_port)
        
            serial_port->cancel();
            serial_port->close();
            serial_port.reset();
        
        else 
        
            WARNING << "serial port is not open";
        
        io_service.stop();
        io_service.reset();
    
    catch (const std::exception &e)
    
        ERROR << "e.what() = " << e.what();
        throw -1;
    

    INFO << "ended";

    return;


void serial_comm::async_read_some (void)

    boost::mutex::scoped_lock lock (mutex); // prevent multiple threads

    INFO << "started";

    std::string data;

    try
    
        if (serial_port.get() == NULL || !serial_port->is_open())
        
            WARNING << "serial port is not open";
            throw -2;
        

        serial_port->async_read_some(
            boost::asio::buffer(read_buffer, MAX_RESPONSE_SIZE),
            boost::bind(
                &serial_comm::data_received,
                this, boost::asio::placeholders::error, 
                boost::asio::placeholders::bytes_transferred
            )
        );
        // start io_service run thread after giving it work
        boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
    
    catch (const std::exception &e)
    
        ERROR << "e.what() = " << e.what();
        throw -1;
    

    INFO << "ended";

    return;


void serial_comm::data_received(const boost::system::error_code& error, size_t bytes_transferred)

    boost::mutex::scoped_lock lock(mutex);  // prevent multiple thread access

    INFO << "started";

    try
    
        if (serial_port.get() == NULL || !serial_port->is_open())
        
            WARNING << "serial port is not open";
            throw -2;
        
        if (error) 
        
            ERROR << "error.message() >> " << error.message().c_str();
            throw -3;
                   
        for (unsigned int i = 0; i < bytes_transferred; ++i) 
            serial_read_data += read_buffer[i];
               
        INFO << "bytes_transferred = " << bytes_transferred << "; serial_read_data = " << serial_read_data; 
        serial_data_read_complete = true;
    
    catch (const std::exception &e)
    
        ERROR << "e.what() = " << e.what();
        throw -1;
    

    // prevent io_service from returning due to lack of work    
    serial_port->async_read_some(
        boost::asio::buffer(read_buffer, MAX_RESPONSE_SIZE),
        boost::bind(
            &serial_comm::data_received,
            this, boost::asio::placeholders::error, 
            boost::asio::placeholders::bytes_transferred
        )
    );

    INFO << "ended";

    return;

ma​​in.cpp

#include "../include/serial_comm.hpp"

int main(void) 

    serial_comm _serial_comm;

    try
           
        _serial_comm.open_serial_port("/dev/ttyS0", 9600);
        _serial_comm.async_read_some(); // this function will always check for data
        loop:       
        while (!_serial_comm.serial_data_read_complete)
        
            sleep(1);
        
        INFO << "_serial_comm.serial_read_data = " << _serial_comm.serial_read_data;
        _serial_comm.serial_read_data.clear();
        _serial_comm.serial_data_read_complete = false;
        goto loop;      
    
    catch (int error)
    
        ERROR << "error >> " << error;
        return;
    

    FATAL << "main ended";

    return;

【讨论】:

“基本上我的问题是在同一个函数中 async_read_some 之后没有启动 io_service 线程。你能怪我吗?这东西不是很清楚。”不,我不能怪你。文档令人震惊。因此我为什么在这里....但是您的评论强调了为什么我的代码不起作用,所以谢谢!

以上是关于Boost ASIO,未调用 async_read_some 回调的主要内容,如果未能解决你的问题,请参考以下文章

Boost.Asio - 轮询命名管道

boost::asio::async_read 不回调我的处理函数

优雅地取消 boost::asio::async_read

boost::asio::async_read 无限循环,接收数据为零字节

如何在到达终止字符时返回 boost::asio::async_read

boost asio async_read中的随机EOF