C++ asio::read 超时

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13126776/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 16:59:49  来源:igfitidea点击:

asio::read with timeout

c++boosttimeoutserial-portboost-asio

提问by Chris K.

I need to know how to read (sync or async doesn't matters) with a timeout. I want to check if a device is connected with a serial port or not.

我需要知道如何超时读取(同步或异步无关紧要)。我想检查设备是否与串口连接。

For that I use asio::writeand then I wait for the response of the device.

为此,我使用asio::write然后等待设备的响应。

If a device is connected asio::read(serial, boost::asio::buffer(&r,1))works fine but if there is no device the program stops, which is is why I need the timeout

如果设备连接asio::read(serial, boost::asio::buffer(&r,1))工作正常但如果没有设备程序停止,这就是我需要超时的原因

I know that I need a deadline_timerbut I have no idea how to use it in the async_readfunction.

我知道我需要一个,deadline_timer但我不知道如何在async_read函数中使用它。

An example of how it works would be really helpful.

它如何工作的一个例子将非常有帮助。

I know that there are many similar threads and I read lot of them but I can't find a solution that helps me solving my problem!

我知道有很多类似的线程,我阅读了很多,但我找不到可以帮助我解决问题的解决方案!

回答by Robert Hegner

The code posted by Igor R.did not compile for me. Here is my improved version of his code, which works perfectly. It uses lambdas to get rid of the set_resulthelper function.

Igor R. 发布代码没有为我编译。这是我对他的代码的改进版本,效果很好。它使用 lambdas 来摆脱set_result辅助函数。

template <typename SyncReadStream, typename MutableBufferSequence>
void readWithTimeout(SyncReadStream& s, const MutableBufferSequence& buffers, const boost::asio::deadline_timer::duration_type& expiry_time)
{
    boost::optional<boost::system::error_code> timer_result;
    boost::asio::deadline_timer timer(s.get_io_service());
    timer.expires_from_now(expiry_time);
    timer.async_wait([&timer_result] (const boost::system::error_code& error) { timer_result.reset(error); });

    boost::optional<boost::system::error_code> read_result;
    boost::asio::async_read(s, buffers, [&read_result] (const boost::system::error_code& error, size_t) { read_result.reset(error); });

    s.get_io_service().reset();
    while (s.get_io_service().run_one())
    { 
        if (read_result)
            timer.cancel();
        else if (timer_result)
            s.cancel();
    }

    if (*read_result)
        throw boost::system::system_error(*read_result);
}

回答by Vikas

You don't use deadline_timerin async_read. But you can start two async processes:

你不用deadline_timerin async_read。但是您可以启动两个异步进程:

  1. An async_readprocess on serial port. boost::asio::serial_porthas a cancelmethod that cancels all async operations on it.
  2. A deadline timer with required timeout. In completion handler for deadline_timeryou can cancelthe serial port. This should close the async_readoperation and call its completion handler with an error.
  1. async_read串行端口上的进程。boost::asio::serial_port有一个取消方法,可以取消对它的所有异步操作。
  2. 具有所需超时的截止时间计时器。在完成处理程序中,deadline_timer您可以cancel使用串行端口。这应该关闭async_read操作并在错误时调用其完成处理程序。

Code:

代码:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/array.hpp>

class timed_connection
{
    public:
        timed_connection( int timeout ) :
            timer_( io_service_, boost::posix_time::seconds( timeout ) ),
            serial_port_( io_service_ )
        {
        }

        void start()
        {
              timer_.async_wait
                (
                 boost::bind
                 (
                  &timed_connection::stop, this
                 )
                );

            // Connect socket
            // Write to socket

            // async read from serial port
            boost::asio::async_read
                (
                 serial_port_, boost::asio::buffer( buffer_ ),
                 boost::bind
                 (
                  &timed_connection::handle_read, this,
                  boost::asio::placeholders::error
                 )
                );

            io_service_.run();
        }

    private:
        void stop()
        {  
            serial_port_.cancel();
        }

        void handle_read ( const boost::system::error_code& ec)
        {  
            if( ec )
            {  
                // handle error
            }
            else
            {  
                // do something
            }
        }

    private:
        boost::asio::io_service io_service_;
        boost::asio::deadline_timer timer_;
        boost::asio::serial_port serial_port_;
        boost::array< char, 8192 > buffer_;
};

int main()
{
    timed_connection conn( 5 );
    conn.start();

    return 0;
}

回答by Igor R.

Once upon a time, the library author proposed the following way to read synchronously with timeout(this example involves tcp::socket, but you can use serial port instead):

曾几何时,库作者提出了下面这种超时同步读取的方式(本例涉及tcp::socket,但可以改用串口):

  void set_result(optional<error_code>* a, error_code b) 
  { 
    a->reset(b); 
  } 


  template <typename MutableBufferSequence> 
  void read_with_timeout(tcp::socket& sock, 
      const MutableBufferSequence& buffers) 
  { 
    optional<error_code> timer_result; 
    deadline_timer timer(sock.io_service()); 
    timer.expires_from_now(seconds(1)); 
    timer.async_wait(boost::bind(set_result, &timer_result, _1)); 


    optional<error_code> read_result; 
    async_read(sock, buffers, 
        boost::bind(set_result, &read_result, _1)); 

    sock.io_service().reset(); 
    while (sock.io_service().run_one()) 
    { 
      if (read_result) 
        timer.cancel(); 
      else if (timer_result) 
        sock.cancel(); 
    } 


    if (*read_result) 
      throw system_error(*read_result); 
  } 

回答by TC1

There's no one or easyanswer per se, since even if you do an async read, the callback never gets called and you now have a loose thread somewhere around.

本身没有一个或简单的答案,因为即使您进行异步读取,也永远不会调用回调,并且您现在在某处有一个松散的线程。

You're right in assuming that deadline_timeris one of the possible solutions, but it takes some fiddling and shared state. There's the blocking TCP example, but that's for async_connectand there's a cool thing about it returning when it has nothing to do. readwon't do that, worst case scenario -- it'll crash & burn 'cause of the invalid resource. So the deadline timerthing is one of your options, but there's actually an easier one, that goes somewhat like this:

您认为这deadline_timer是可能的解决方案之一是正确的,但它需要一些摆弄和共享状态。有一个阻塞 TCP 的例子,但那是为了async_connect当它无事可做时返回它有一个很酷的事情。read不会那样做,最坏的情况——它会因为无效资源而崩溃和烧毁。所以这deadline timer件事是你的选择之一,但实际上有一个更简单的选择,有点像这样:

boost::thread *newthread = new boost::thread(boost::bind(&::try_read));
if (!newthread->timed_join(boost::posix_time::seconds(5))) {
    newthread->interrupt();
}

Basically, do the read in another thread and kill it off if it times out. You should read up on Boost.Threads.

基本上,在另一个线程中读取并在超时时将其终止。你应该阅读Boost.Threads

If you interrupt it, make sure the resources are all closed.

如果您中断它,请确保资源全部关闭。