一个优雅的解决方案是使用协程。Boost.Asio支持stackless协程,它们引入了一小组伪关键字,以及stackful协程,它们使用Boost.Coroutine。
无栈协程
无栈协程引入了一组伪关键字预处理宏,使用类似于达夫设备的技术实现了一个switch语句。文档详细介绍了每个关键字。
使用无栈协程实现原始问题(连接->读取头文件->读取正文)可能如下所示:
struct session
: boost::asio::coroutine
{
boost::asio::ip::tcp::socket socket_;
std::vector<char> buffer_;
void operator()(boost::system::error_code ec = boost::system::error_code(),
std::size_t length = 0)
{
if (ec)
{
print_error(ec);
return;
}
reenter (this)
{
yield socket_.async_connect(endpoint_, *this);
while (!shutdown_)
{
buffer_.resize(fixed_header_size);
yield socket_.async_read(boost::asio::buffer(buffer_), *this);
std::size_t body_size = parse_header(buffer_, length);
if (!body_size) return;
buffer_.resize(body_size);
yield socket_.async_read(boost::asio::buffer(buffer_), *this);
body_handler_(buffer_, length);
}
socket_.shutdown(tcp::socket::shutdown_both, ec);
}
}
}
堆栈式协程
使用spawn()
函数创建堆栈式协程。当使用堆栈式协程实现时,原始问题可能如下所示:
boost::asio::spawn(io_service, [&](boost::asio::yield_context yield)
{
boost::system::error_code ec;
boost::asio::ip::tcp::socket socket(io_service);
socket.async_connect(endpoint, yield[ec]);
if (ec)
{
print_error(ec);
return;
}
std::vector<char> buffer;
while (!shutdown)
{
buffer.resize(fixed_header_size);
std::size_t bytes_transferred = socket.async_read(
boost::asio::buffer(buffer), yield[ec]);
if (ec)
{
print_error(ec);
return;
}
std::size_t body_size = parse_header(buffer, bytes_transferred);
if (!body_size) return;
buffer.resize(body_size);
bytes_transferred =
socket.async_read(boost::asio::buffer(buffer), yield[ec]);
if (ec)
{
print_error(ec);
return;
}
body_handler_(buffer, length);
}
socket.shutdown(tcp::socket::shutdown_both, ec);
});