2016-07-18 167 views
1

我的程序充當客戶端可以連接的服務器。一旦連接了一個客戶端,他將每5秒鐘從服務器獲取更新。這是write -function是每5秒被稱爲新的數據發送到客戶端:Boost.Asio:異步操作超時

void NIUserSession::write(std::string &message_orig) 
{ 
    std::cout << "Writing message" << std::endl; 

    std::shared_ptr<std::string> message = std::make_shared<std::string>(message_orig); 
    message->append("<EOF>"); 
    boost::system::error_code ec; 
    boost::asio::async_write(this->socket_, boost::asio::buffer(*message), 
     boost::asio::transfer_all(), boost::bind(&NIUserSession::writeHandler, 
       this, boost::asio::placeholders::error, 
       boost::asio::placeholders::bytes_transferred(), 
       message 
       )); 
} 

void NIUserSession::writeHandler(const boost::system::error_code &error, std::size_t bytes_transferred, std::shared_ptr<std::string> message) 
{ 
    std::cout << "Write Handler" << std::endl; 
    if(error) 
    { 
     std::cout << "Write handler error: " << error.message() << std::endl; 
     this->disconnect(); 
    } 
} 

void NIUserSession::disconnect() 
{ 
    std::cout << "Disconnecting client, cancling all write and read operations." << std::endl; 
    this->socket_.lowest_layer().cancel(); 

    delete this; 
} 

如果在寫操作的錯誤在服務器和客戶端之間的連接被關閉,所有異步操作是cancled(this->socket_.lowest_layer().cancel();)。如果連接超時,將不會立即調用writeHandler。相反,寫入操作「疊加」直到第一個到達writeHandler

這應該是程序的正常輸出:

Writing message 
Write Handler 
... Other stuff ... 
... Other stuff ... 
Writing message 
Write Handler 

如果連接超時,這是發生了什麼:

Writing message 
Write Handler 
Write handler error: Connection timed out 
Disconnecting client, cancling all write and read operations. 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Write Handler 
Segmentation fault 

最後,分段錯誤上升。我認爲這是因爲disconnect被調用,而其他異步操作仍在進行中。 我想我可以通過在第一次異步操作失敗後直接使用this->socket_.lowest_layer().cancel();來避免它,但它不起作用。

如何避免分段錯誤?

回答

3

那麼,在取消操作時,您不應該刪除this,因爲仍然會調用待處理的I/O操作的回調,然後訪問this會導致未定義的行爲。有多種方法可以解決這個問題:

  1. 在您確實知道之前的數據已寫入之前,請勿寫入數據。如果未完成的寫入仍處於待處理狀態,並且在未完成的寫入操作完成時實際將其寫入處理程序,則可以將傳遞給NIUserSession::writestd::string實例排隊。這樣你就不會有多個I/O操作。
  2. 繼承自std::enable_shared_from_this並通過shared_from_this()而不是this撥打電話async_write(這是Boost asynchronous TCP daytime server example所做的)。這種方式等待I/O操作將保持對你的類的引用,並且如果它們全部完成,則會調用析構函數。
+0

它不會導致分段錯誤。它會導致[未定義行爲](https://en.wikipedia.org/wiki/Undefined_behavior)。否則,+1 – sehe

+0

使用異步操作時,您需要非常小心對象的生命週期。 'boost :: asio' [示例](http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/examples/cpp11_examples.html)使用'shared_ptr'有很好的理由,參見:[啓動asio異步功能和共享ptrs](http://stackoverflow.com/questions/11356742/boost-async-functions-and-shared-ptrs/19622084#19622084) – kenba

+0

@sehe - 謝謝,編輯:)我不好使用不正確的措辭。 –