2016-03-22 32 views
0

我有以下代碼,試圖編寫一個異步客戶端。Asio:防止異步客戶端被刪除?

問題是,在main()中,Client在try-catch塊中被刪除,因爲執行離開了範圍。

我試圖想出解決這個問題的方法,比如加入while(true),但我不喜歡這種方法。另外,我不喜歡getchar()

由於呼叫的異步性質,connect()loop()都立即返回。

我該如何解決這個問題?

#include <iostream> 
#include <thread> 
#include <string> 

#include <boost\asio.hpp> 
#include <Windows.h> 

#define DELIM "\r\n" 

using namespace boost; 


class Client { 
public: 
    Client(const std::string& raw_ip_address, unsigned short port_num) : 
     m_ep(asio::ip::address::from_string(raw_ip_address), port_num), m_sock(m_ios) 
    { 
     m_work.reset(new asio::io_service::work(m_ios)); 
     m_thread.reset(new std::thread([this]() { 
      m_ios.run(); 
     })); 

     m_sock.open(m_ep.protocol()); 
    } 

    void connect() 
    { 
     m_sock.async_connect(m_ep, [this](const system::error_code& ec) 
     { 
      if (ec != 0) { 
       std::cout << "async_connect() error: " << ec.message() << " (" << ec.value() << ") " << std::endl; 
       return; 
      } 

      std::cout << "Connection to server has been established." << std::endl; 
     }); 
    } 

    void loop() 
    { 
     std::thread t = std::thread([this]() 
     { 
      recv(); 
     }); 

     t.join(); 
    } 

    void recv() 
    { 
     asio::async_read_until(m_sock, buf, DELIM, [this](const system::error_code& ec, std::size_t bytes_transferred) 
     { 
      if (ec != 0) { 
       std::cout << "async_read_until() error: " << ec.message() << " (" << ec.value() << ") " << std::endl; 
       return; 
      } 

      std::istream is(&buf); 
      std::string req; 
      std::getline(is, req, '\r'); 
      is.get(); // discard newline 

      std::cout << "Received: " << req << std::endl; 

      if (req == "alive") { 
       recv(); 
      } 
      else if (req == "close") { 
       close(); 
       return; 
      } 
      else { 
       send(req + DELIM); 
      } 
     }); 
    } 

    void send(std::string resp) 
    { 
     auto s = std::make_shared<std::string>(resp); 
     asio::async_write(m_sock, asio::buffer(*s), [this, s](const system::error_code& ec, std::size_t bytes_transferred) 
     { 
      if (ec != 0) { 
       std::cout << "async_write() error: " << ec.message() << " (" << ec.value() << ") " << std::endl; 
       return; 
      } 
      else { 
       recv(); 
      } 
     }); 
    } 

    void close() 
    { 
     m_sock.close(); 
     m_work.reset(); 
     m_thread->join(); 
    } 

private: 
    asio::io_service m_ios; 
    asio::ip::tcp::endpoint m_ep; 
    asio::ip::tcp::socket m_sock; 
    std::unique_ptr<asio::io_service::work> m_work; 
    std::unique_ptr<std::thread> m_thread; 

    asio::streambuf buf; 
}; 

int main() 
{ 
    const std::string raw_ip_address = "127.0.0.1"; 
    const unsigned short port_num = 8001; 

    try { 
     Client client(raw_ip_address, port_num); 
     client.connect(); 
     client.loop(); 
    } 
    catch (system::system_error &err) { 
     std::cout << "main() error: " << err.what() << " (" << err.code() << ") " << std::endl; 
     return err.code().value(); 
    } 
    return 0; 
} 

回答

1

你還沒有真正理解asio如何工作。通常在主線程()你將調用io_service::run()(這將處理所有的異步事件。)

爲了保證Client的壽命,使用shared_ptr<>並確保該共享指針在處理程序使用。例如..

io_service service; 

{ 
    // Create the client - outside of this scope, asio will manage 
    // the life time of the client 
    auto client = make_shared<Client>(service); 
    client->connect(); // setup the connect operation.. 
} 
// Now run the io service event loop - this will block until there are no more 
// events to handle 
service.run(); 

現在你需要重構自己Client代碼:

class Client : public std::enable_shared_from_this<Client> { 

Client(io_service& service): socket_(service) ... 
{ } 

void connect() { 
    // By copying the shared ptr to the lambda, the life time of 
    // Client is guaranteed 
    socket_.async_connect(endpoint_, [self = this->shared_from_this()](auto ec) 
    { 
     if (ec) { 
     return; 
     } 

     // Read 
     self->read(self); 
    }); 
} 

void read(shared_ptr<Client> self) { 
    // By copying the shared ptr to the lambda, the life time of 
    // Client is guaranteed 
    asio::async_read_until(socket_, buffer_, DELIM, [self](auto ec, auto size) 
    { 
    if (ec) { 
     return; 
    } 
    // Handle the data 
    // Setup the next read operation 
    self->read(self) 
    }); 
} 
}; 

您有執行讀操作的線程 - 這是沒有必要的。這將註冊一個異步讀取操作並立即返回。您需要註冊一個新的讀取操作以繼續讀取插槽(如我已經勾畫出的那樣..)