2016-02-14 48 views
4

我正试图围绕boost :: asio中的资源管理进行包装。我看到在相应的套接字已经被销毁之后调用的回调函数。这方面的一个很好的例子是在升压:: ASIO正式例如:http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/example/cpp11/chat/chat_client.cpp如何正确关闭boost :: asio中的套接字?

我特别关注与关闭方法:

void close() 
{ 
    io_service_.post([this]() { socket_.close(); }); 
} 

如果调用此函数事后销毁chat_client实例保存socket_, socket_将在调用close方法之前被破坏。此外,还可以在chat_client销毁后调用任何待处理的async_ *回调。

你会如何正确处理这个问题?

+0

到您的[现已删除评论](http://stackoverflow.com/questions/35393015/how-do-you-correctly-close-sockets-in-boostasio#comment58492034_35393083)_“关闭套接字不肯定不会导致所有待处理的回调被调用时出现错误。“_:那么你会怎么说呢? – sehe

回答

6

你可以做socket_.close();几乎任何你想要的时间,但你应该记住某些时刻:

  • 如果你有线程,这个调用应该与包裹,或者你可能会崩溃。见boost strand documentation
  • 无论你做什么close请记住, io_service已经可以有排队的处理程序。无论如何,它们都会被调用,并带有旧状态/错误代码。
  • close可以抛出异常。
  • close不包括ip :: tcp :: socket销毁。它 只是关闭系统插座。
  • 您必须自己管理对象生命周期 ,以确保只有在没有更多处理程序的情况下对象才会被销毁。通常这是通过enable_shared_from_this 您的Connectionsocket对象完成的。
+2

这太复杂了,普通的凡人程序员应该避免关闭boost asio套接字,最好不要打开它们。 – sqrt163

3

调用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_; 
    ... 
} 

应用程序负责管理对象的生命周期在其上运行和处理程序是依赖的。该chat client example由低保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) 
    { 
    ... 
    } 
} 

一个常见的成语是管理对象寿命使I/O对象由从enable_shared_from_this<>继承的单个类来管理。当一个类继承自enable_shared_from_this时,它提供了一个shared_from_this()成员函数,该函数返回管理this的有效shared_ptr实例。将shared_ptr的副本传递给完成处理程序,例如lambdas中的捕获列表或作为实例句柄传递给bind(),从而导致I/O对象的生命周期至少与处理程序一样长。有关使用此方法的示例,请参阅Boost.Asio asynchronous TCP daytime server教程。

相关问题