2012-02-09 2 views
5

비동기 부스트 ASIO TCP 서버를 종료하는 적절한 방법은 무엇입니까? 내 현재 솔루션은 일반적으로 소멸자에서 교착 상태가 발생합니다. 왜?asio tcp server를 올바르게 종료하는 방법은 무엇입니까?

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

답변

3

이 "교착 상태"연결이 여전히 shared_from_this()이 전달 작업이 대기하고 있기 때문에, 파괴 될 수 없습니다 때문입니다.

각 연결의 소켓 종료 (..) 및 종료 (..)를 호출하십시오. 그런 다음 eof 또는 operation_aborted 신호를 보내는 완료를 기다립니다.

+0

다른 스레드에서 소켓 종료 및 닫기를 호출 할 수 있습니까? 또는 io_service 스레드에 있어야합니까? – ronag

+1

[TCP 소켓 문서] (http://www.boost.org/doc/libs/1_48_0/doc/html/boost_asio/reference/ip__tcp/socket.html)에 따르면 액세스가 동기화되어야합니다 (또는 액세스 할 때마다 같은 스레드에 의해). – Simon

관련 문제