你如何正确关闭boost :: asio中的套接字?

Jul*_*ina 10 c++ sockets boost boost-asio c++11

我试图在boost :: asio中围绕资源管理.我看到相应的套接字已被销毁后调用回调.一个很好的例子是在boost :: asio官方示例中:http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/example/cpp11/chat/chat_client.cpp

我特别关注密切的方法:

void close()
{
   io_service_.post([this]() { socket_.close(); });
}
Run Code Online (Sandbox Code Playgroud)

如果调用此函数事后销毁保存socket_ chat_client例如,被称为其上的接近方法之前socket_将被破坏.在chat_client被销毁之后,也可以调用任何挂起的async_*回调.

你怎么能正确处理这个?

PSI*_*Alt 11

socket_.close();几乎可以随时做任何事情,但你应该记住一些时刻:

  • 如果你有线程,这个调用应该用strand包装,否则你可能崩溃.请参阅boost strand文档.
  • 每当你close记住 io_service已经有排队的处理程序时.无论如何,它们将被调用旧的状态/错误代码.
  • close 可以抛出异常.
  • close不包括ip :: tcp :: socket销毁.它只是关闭系统套接字.
  • 您必须自己管理对象生存期,以确保只有在没有更多处理程序时才会销毁对象.通常这是通过 您或对象上的enable_shared_from_this来完成的.Connectionsocket

  • 它太复杂了,普通凡人程序员应该避免关闭增强asio套接字,最好不要打开它们. (13认同)

Tan*_*ury 6

调用socket.close()不会破坏套接字.但是,应用程序可能需要管理操作和完成处理程序所依赖的对象的生命周期,但这不一定是套接字对象本身.例如,考虑一个client包含缓冲区,套接字的类,并且具有单个未完成的读操作,其完成处理程序为client::handle_read().可以close()并明确地销毁套接字,但缓冲区和client实例必须保持有效,直到至少调用处理程序:

class client
{
  ...

  void read()
  {
    // Post handler that will start a read operation.
    io_service_.post([this]() {
      async_read(*socket, boost::asio::buffer(buffer_);
        boost::bind(&client::handle_read, this,
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
    });
  }

  void handle_read(
    const boost::system::error_code& error,
    std::size_t bytes_transferred
  )
  {
    // make use of data members...if socket_ is not used, then it
    // is safe for socket to have already been destroyed.
  }

  void close()
  {
    io_service_.post([this]() {
      socket_->close();
      // As long as outstanding completion handlers do not
      // invoke operations on socket_, then socket_ can be 
      // destroyed.
      socket_.release(nullptr);
    });
  }

private:
  boost::asio::io_service& io_service_;

  // Not a typical pattern, but used to exemplify that outstanding
  // operations on `socket_` are not explicitly dependent on the 
  // lifetime of `socket_`.
  std::unique_ptr<boost::asio::socket> socket_;
  std::array<char, 512> buffer_;
  ...
}
Run Code Online (Sandbox Code Playgroud)

应用程序负责管理操作和处理程序所依赖的对象的生命周期.在聊天客户端例如通过低保实现这一chat_client实例被破坏不再使用,通过等待io_service.run()的线程内返回join():

int main(...)
{
  try
  {
    ...

    boost::asio::io_service io_service;
    chat_client c(...);

    std::thread t([&io_service](){ io_service.run(); });

    ...

    c.close();
    t.join(); // Wait for `io_service.run` to return, guaranteeing
              // that `chat_client` is no longer in use.
  }           // The `chat_client` instance is destroyed.
  catch (std::exception& e)
  {
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

管理对象生存期的一个常见习惯是让I/O对象由继承自的单个类管理enable_shared_from_this<>.当一个类继承时enable_shared_from_this,它提供了一个shared_from_this()返回有效shared_ptr实例管理的成员函数this.它的副本shared_ptr被传递给完成处理程序,例如lambda中的捕获列表或作为实例句柄传递bind(),导致I/O对象的生命周期至少与处理程序一样长.有关使用此方法的示例,请参阅Boost.Asio 异步TCP日间服务器教程.