【问题标题】:boost asio async_connect success after close关闭后提升 asio async_connect 成功
【发布时间】:2013-01-13 05:55:52
【问题描述】:

单线程应用程序。

并非每次都会发生,只有在高负载 1.5 小时后才会发生。

  1. tcp::socket::async_connect
  2. tcp::socket::close(按deadline_timer)
  3. async_connect_handler 给出成功错误代码(百万次之一),但套接字由 (2) 关闭。 99.999% 的时间它给出 errno=125 (ECANCELED)。

socket 实现或 boost asio 是否有可能以某种方式做到这一点:

  1. async_connect
  2. 异步成功发布到 io_service
  3. 按计时器关闭
  4. 异步成功由我处理,不受关闭影响

现在通过在我的变量中保存状态来解决,忽略接受成功。

Linux 2.6 (fedora)。 提升 1.46.0

PS:我可能会遇到一些错误......但如果不是这样,可以顺利运行几天。

【问题讨论】:

  • 是的,如果完成处理程序已经在队列中 - 它将被调用。
  • 顺便说一句,您不要尝试在执行io_service::run() 的线程以外的线程上调用tcp::socket 成员函数

标签: c++ sockets boost boost-asio


【解决方案1】:

正如 Igor 在 cmets 中提到的,完成处理程序已经排队。

这种情况是操作执行与调用处理程序之间的时间分离的结果。 io_service::run()io_service::run_one()io_service::poll()io_service::poll_one() 的文档专门针对处理程序,而不是操作。在该场景中,socket::async_connect() 操作和deadline_timer::async_wait() 操作在同一个事件循环迭代中完成。这会导致两个处理程序都以未指定的顺序添加到 io_service 以进行延迟调用。

考虑以下强调场景的 sn-p:

void handle_wait(const boost::system::error_code& error)
{
  if (error) return;
  socket_.close();
}

timer_.expires_from_now(boost::posix_time::seconds(30));
timer_.async_wait(&handle_wait);
socket_.async_connect(endpoint_, handle_connect);
boost::this_thread::sleep(boost::posix_time::seconds(60));
io_service_.run_one();

io_service_.run_one() 被调用时,socket::async_connect()deadline_timer::async_wait() 操作可能已经完成,导致handle_waithandle_connect 准备好以未指定的顺序从io_service 中调用。为了正确处理这个未指定的顺序,需要在handle_wait()handle_connect() 中发生额外的逻辑来查询当前状态,并确定是否调用了另一个处理程序,而不是仅仅依赖于状态(error_code)操作。

确定其他处理程序是否已调用的最简单方法是:

  • handle_connect() 中,通过is_open() 检查套接字是否仍然打开。如果套接字仍然打开,则 handle_timer() 尚未被调用。向handle_timer() 表明handle_connect() 已运行的一种简洁方法是更新到期时间。
  • handle_timer(),检查是否过期。如果这是真的,那么handle_connect() 还没有运行,所以关闭套接字。

生成的处理程序可能如下所示:

void handle_wait(const boost::system::error_code& error)
{
  // On error, return early.
  if (error) return;

  // If the timer expires in the future, then connect handler must have
  // first.
  if (timer_.expires_at() > deadline_timer::traits_type::now()) return;

  // Timeout has occurred, so close the socket.
  socket_.close();
}

void handle_connect(const boost::system::error_code& error)
{
  // The async_connect() function automatically opens the socket at the start
  // of the asynchronous operation. If the socket is closed at this time then
  // the timeout handler must have run first.
  if (!socket_.is_open()) return;

  // On error, return early.
  if (error) return;

  // Otherwise, a connection has been established.  Update the timer state
  // so that the timeout handler does not close the socket.
  timer_.expires_at(boost::posix_time::pos_infin);
}

Boost.Asio 提供了一些examples 用于处理超时。

【讨论】:

  • 是的,就像我想的那样。另外我想补充一点,人们应该小心关闭的错误代码,它可能会在成功的 async_read_XX 或 async_write_XXX 处理程序中失败。给我的教训 =)
【解决方案2】:

我接受 twsansbury 的回答,只是想补充一些信息。

关于shutdown():

void async_recv_handler( boost::system::error_code ec_recv, std::size_t count )
{
    if ( !m_socket.is_open() )
        return; // first time don't trust to ec_recv
    if ( ec_recv )
    {
        // oops, we have error
        // log
        // close
        return;
    }
    // seems that we are just fine, no error in ec_recv, we can gracefully shutdown the connection
    // but shutdown may fail! this check is working for me
    boost::system::error_code ec_shutdown;
    // second time don't trusting to ec_recv
    m_socket.shutdown( t, ec_shutdown );
    if ( !ec_shutdown )
        return;
    // this error code is expected
    if ( ec_shutdown == boost::asio::error::not_connected )
       return;
    // other error codes are unexpected for me
    // log << ec_shutdown.message()
    throw boost::system::system_error(ec_shutdown);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-05
    • 1970-01-01
    • 1970-01-01
    • 2016-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多