2013-03-01 82 views
2

我的程序总是习惯依赖于平台的同步接收,阻碍执行,直到超时或接收事件,喜欢的东西:async_read_some效仿syncronous超时收到

recv(buf, size, timeout);

现在我要替换此代码促使它跨平台。我找到了解决方案,但我认为这很难看(与单一函数调用相比)。我写了这样的:

void IPV4_HOST::recv_timer_handler(const boost::system::error_code & e) 
{ 
    if (e.value() == boost::system::errc::success) { 
     f_recv_timeout = true; 
     asio_socket.cancel(); 
    } 
} 

void IPV4_HOST::recv_handler(const boost::system::error_code & , size_t bytes) 
{ 
    recv_timer.cancel(); 
    bytes_received = bytes; 
} 

int IPV4_HOST::receive(void * buf, size_t buf_size, TIME::INTERVAL timeout) 
{ 
    f_recv_timeout = false; 
    bytes_received = 0; 

    recv_timer.expires_from_now(timeout.get_boost_milli()); 
    recv_timer.async_wait(boost::bind(&IPV4_HOST::recv_timer_handler, this, boost::asio::placeholders::error)); 

    asio_socket.async_read_some(boost::asio::buffer(buf, buf_size), 
           boost::bind(&IPV4_HOST::recv_handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); 

    io_service.run(); 

    if (f_recv_timeout) 
     throw IO::TimeoutReceiveException(); 

    return bytes_received; 
} 

请问你能否告诉我,我是对的还是不对?有没有简单的方法来做到这一点?

+1

请参阅库作者的以下答案:http://boost.2283326.n4.nabble.com/asio-setting-a-timeout-on-blocking-write-s-read-s-tt2624802.html# a2624803 – 2013-03-01 13:01:27

+0

这几乎等于我所做的,但无论如何谢谢 – 2013-03-01 13:51:47

回答

4

这是在正确的方向,但也有一些细微的问题需要考虑:

  • 如果任何其他工作过帐到io_service,然后IPV4_HOST::receive()将开始这项工作的处理。
  • io_service::run()在正常条件下返回时,暗示io_service已停止。后续调用run(),run_one(),poll()poll_one()将立即返回,除非io_service为reset()
  • 可以同时完成两个异步操作,使两个完成处理程序都可以成功运行。这种行为在deadline_timer::cancel()的备注部分加以强调;但是,所有异步操作都会显示此behavior。在现有的代码中,当bytes_received大于零时,可能导致IO::TimeoutReceiveException被抛出。

一个解决方案来处理io_service细节,以及执行的非确定性以便与完成处理,可能看起来是这样的:

void IPV4_HOST::recv_timer_handler(const boost::system::error_code & e) 
{ 
    timer_handled = true; 
    if (!e) { 
    f_recv_timeout = true; 
    asio_socket.cancel(); 
    } 
} 

void IPV4_HOST::recv_handler(const boost::system::error_code &, 
          size_t bytes) 
{ 
    recv_handled = true; 
    recv_timer.cancel(); 
    bytes_received = bytes; 
} 

int IPV4_HOST::receive(void * buf, size_t buf_size, TIME::INTERVAL timeout) 
{ 
    timer_handled = false; 
    recv_handled = false; 
    f_recv_timeout = false; 
    bytes_received = 0; 

    recv_timer.expires_from_now(timeout.get_boost_milli()); 
    recv_timer.async_wait(
    boost::bind(&IPV4_HOST::recv_timer_handler, this, 
       boost::asio::placeholders::error)); 

    asio_socket.async_read_some(
    boost::asio::buffer(buf, buf_size), 
    boost::bind(&IPV4_HOST::recv_handler, this, 
       boost::asio::placeholders::error, 
       boost::asio::placeholders::bytes_transferred)); 

    // If a handler has not ran, then keep processing work on the io_service. 
    // We need to consume both handlers so that old handlers are not in the 
    // io_service the next time receive is called. 
    while (!timer_handled || !recv_handled) 
    { 
    io_service.run_one(); 
    } 

    // If the io_service has stopped (due to running out of work), then reset 
    // it so that it can be run on next call to receive. 
    if (io_service.stopped()) 
    io_service.reset(); 

    // If no bytes were received and the timeout occurred, then throw. This 
    // handles the case where both a timeout and receive occurred at the 
    // same time. 
    if (!bytes_received && f_recv_timeout) 
    throw IO::TimeoutReceiveException(); 

    return bytes_received; 
} 

而且,当你试图获取跨 - 平台行为,请阅读basic_stream_socket::cancel()的评论。有一些特定于平台的行为需要注意。

+0

谢谢你的答案,我测试了你的更正代码,现在它的工作非常稳定。我唯一没有得到的是为什么我们应该在重置之前检查io_service.stopped()?为什么我们不能每次都调用重置?如果我们在其他处理程序的其他地方使用io_service,是否是这种情况? – 2013-03-01 14:44:54

+1

正确。如果对run(),run_one(),poll()或poll_one()函数有任何未完成的调用,则不能安全地调用reset()。我不确定'io_service'是否被其他处理程序使用,所以选择了更安全的解决方案。 – 2013-03-01 15:47:01