boost asio async_connect success after close

旧街凉风 提交于 2019-12-04 16:03:45

问题


Single-threaded application.

It happens not every time, only after 1.5 hours of high load.

  1. tcp::socket::async_connect
  2. tcp::socket::close (by deadline_timer)
  3. async_connect_handler gives success error_code (one of a million times), but socket is closed by(2). 99.999% of time it gives errno=125 (ECANCELED).

Is it possible that socket implementation or boost asio somehow do this:

  1. async_connect
  2. async success posted to io_service
  3. close by timer
  4. async success handled by me, not affected by close

Right now solved by saving state in my variables, ignoring accept success.

Linux 2.6 (fedora). Boost 1.46.0

PS: ofcouse possible bug on my part... But runs smoothly for days if not this.


回答1:


As Igor mentions in the comments, the completion handler is already queued.

This scenario is the result of a separation in time between when an operation executes and when a handler is invoked. The documentation for io_service::run(), io_service::run_one(), io_service::poll(), and io_service::poll_one() is specific to mention handlers, and not operations. In the scenario, the socket::async_connect() operation and deadline_timer::async_wait() operation complete in the same event loop iteration. This results in both handlers being added to the io_service for deferred invocation, in an unspecified order.

Consider the following snippet that accentuates the scenario:

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();

When io_service_.run_one() is invoked, both socket::async_connect() and deadline_timer::async_wait() operations may have completed, causing handle_wait and handle_connect to be ready for invocation from within the io_service in an unspecified order. To properly handle this unspecified order, additional logic need to occur from within handle_wait() and handle_connect() to query the current state, and determine if the other handler has been invoked, rather than depending solely on the status (error_code) of the operation.

The easiest way to determine if the other handler has invoked is:

  • In handle_connect(), check if the socket is still open via is_open(). If the socket is still open, then handle_timer() has not been invoked. A clean way to indicate to handle_timer() that handle_connect() has ran is to update the expiry time.
  • In handle_timer(), check if the expiry time has passed. If this is true, then handle_connect() has not ran, so close the socket.

The resulting handlers could look like the following:

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 provides some examples for handling timeouts.




回答2:


I accept twsansbury's answer, just want to add some more info.

About 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);
}


来源:https://stackoverflow.com/questions/14586695/boost-asio-async-connect-success-after-close

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!