我的程序充当客户端可以连接的服务器。一旦客户端连接,他将每隔约5秒从服务器获得更新。这是write
- 函数,每隔5秒调用一次,将新数据发送到客户端:
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;
}
如果写操作中出现错误,服务器和客户端之间的连接将被关闭,所有异步操作都会被解析(this->socket_.lowest_layer().cancel();
)。
问题是如果连接超时,将不会立即调用writeHandler
。相反,写操作&#34;堆叠&#34;直到第一个到达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();
来避免它,但它不起作用。
如何避免分段错误?
答案 0 :(得分:3)
嗯,取消操作时不应删除this
,因为仍会调用挂起的I / O操作的回调,然后访问this
会导致未定义的行为。有多种方法可以解决这个问题:
std::string
的{{1}}实例排队,以防未完成的写入仍处于未决状态,然后在未完成的写入操作完成时实际将它们写入处理程序中。这样,您就不会有多个I / O操作。NIUserSession::write
并将std::enable_shared_from_this
而不是shared_from_this()
传递给this
来电(这是Boost asynchronous TCP daytime server example所做的)。那种等待I / O操作的方式将保留对您的类的引用,并且如果所有这些操作都完成,则将调用析构函数。