2012-02-09 24 views
5

¿Cuál es la forma correcta de apagar un servidor asio tcp de refuerzo asíncrono? Mi solución actual suele ser un punto muerto en el destructor. ¿Por qué?¿Cómo cerrar correctamente el servidor asio tcp?

class connection; 

typedef std::set<shared_ptr<connection>> connection_set; 

class connection : public enable_shared_from_this<connection> 
{  
    shared_ptr<tcp::socket>   socket_; 

    std::array<char, 8192>   data_; 

    shared_ptr<connection_set>  connection_set_; 
public: 
    static shared_ptr<connection> create(shared_ptr<tcp::socket> socket, shared_ptr<connection_set> connection_set) 
    { 
     auto con = shared_ptr<connection>(new connection(std::move(socket), std::move(connection_set))); 
     con->read_some(); 
     return con; 
    } 

    void on_next(const event& e) 
    { 
     // async_write_some ... 
    } 

private: 
    connection(shared_ptr<tcp::socket> socket, shared_ptr<connection_set> connection_set) 
     : socket_(std::move(socket)) 
     , connection_set_(std::move(connection_set)) 
    { 
    } 

    void handle_read(const boost::system::error_code& error, size_t bytes_transferred) 
    {  
     if(!error) 
     { 
      on_read(std::string(data_.begin(), data_.begin() + bytes_transferred));  
      read_some(); 
     } 
     else if (error != boost::asio::error::operation_aborted) 
      connection_set_->erase(shared_from_this()); 
     else 
      read_some(); 
    } 

    void handle_write(const shared_ptr<std::vector<char>>& data, const boost::system::error_code& error, size_t bytes_transferred) 
    { 
     if(!error)   
     { 
     } 
     else if (error != boost::asio::error::operation_aborted) 
      connection_set_->erase(shared_from_this()); 
    } 

    void read_some() 
    { 
     socket_->async_read_some(boost::asio::buffer(data_.data(), data_.size()), std::bind(&connection::handle_read, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); 
    } 

    void on_read(std::string str) 
    { 
     // parse the string... 
    } 
}; 

class tcp_observer 
{ 
    boost::asio::io_service    service_; 
    tcp::acceptor      acceptor_; 
    std::shared_ptr<connection_set>  connection_set_; 
    boost::thread      thread_; 

public: 
    tcp_observer(unsigned short port) 
     : acceptor_(service_, tcp::endpoint(tcp::v4(), port)) 
     , thread_(std::bind(&boost::asio::io_service::run, &service_)) 
    { 
     start_accept(); 
    } 

    ~tcp_observer() 
    { 
     // Deadlocks... 
     service_.post([=] 
     { 
      acceptor_.close(); 
      connection_set_->clear(); 
     }); 
     thread_.join(); 
    } 

    void on_next(const event& e) 
    { 
     service_.post([=] 
     { 
      BOOST_FOREACH(auto& connection, *connection_set_) 
       connection->on_next(e); 
     }); 
    } 
private:   
    void start_accept() 
    { 
     auto socket = std::make_shared<tcp::socket>(service_); 
     acceptor_.async_accept(*socket, std::bind(&tcp_observer::handle_accept, this, socket, std::placeholders::_1)); 
    } 

    void handle_accept(const shared_ptr<tcp::socket>& socket, const boost::system::error_code& error) 
    { 
     if (!acceptor_.is_open()) 
      return; 

     if (!error)  
      connection_set_->insert(connection::create(socket, connection_set_)); 

     start_accept(); 
    } 
}; 

Respuesta

3

Este "puntos muertos", porque las conexiones no se va a ser destruido, ya que hay operaciones en las que se aprobó una shared_from_this() aún está pendiente.

Llamar en el cierre de socket de cada conexión (..) y cerrar (..). Luego espere a que finalice, lo que indica eof o operation_aborted.

+0

¿Puedo llamar al cierre del socket y cerrarlo en otro hilo? ¿O tiene que estar en el hilo io_service? – ronag

+1

De acuerdo con [la documentación del zócalo tcp] (http://www.boost.org/doc/libs/1_48_0/doc/html/boost_asio/reference/ip__tcp/socket.html), el acceso debe sincronizarse (o accederse cada vez que lo desee) por el mismo hilo). – Simon

Cuestiones relacionadas