2016-07-18 112 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 - 谢谢,编辑:)我不好使用不正确的措辞。 –