如何使用Boost::asio异步读取std::string?
我正在学习Boost::asio和所有异步的东西。如何异步读取std::string类型的变量如何使用Boost::asio异步读取std::string?,string,boost,asynchronous,boost-asio,String,Boost,Asynchronous,Boost Asio,我正在学习Boost::asio和所有异步的东西。如何异步读取std::string类型的变量user\uuuBoost::asio::buffer(user)仅适用于async\u write(),而不适用于async\u read()。它与向量一起工作,那么它不与字符串一起工作的原因是什么?除了声明char user\uu[max\u len]和使用Boost::asio::buffer(user\uu,max\u len)之外,还有其他方法可以做到这一点吗 另外,从boost::从\u t
user\uuu
Boost::asio::buffer(user)
仅适用于async\u write()
,而不适用于async\u read()
。它与向量一起工作,那么它不与字符串一起工作的原因是什么?除了声明char user\uu[max\u len]
和使用Boost::asio::buffer(user\uu,max\u len)
之外,还有其他方法可以做到这一点吗
另外,从boost::从\u this
中启用\u shared\u,并在async\u read()
和async\u write()
中使用shared\u from\u this()
而不是this
有什么意义?我在例子中看到了很多
以下是我代码的一部分:
class Connection
{
public:
Connection(tcp::acceptor &acceptor) :
acceptor_(acceptor),
socket_(acceptor.get_io_service(), tcp::v4())
{ }
void start()
{
acceptor_.get_io_service().post(
boost::bind(&Connection::start_accept, this));
}
private:
void start_accept()
{
acceptor_.async_accept(socket_,
boost::bind(&Connection::handle_accept, this,
placeholders::error));
}
void handle_accept(const boost::system::error_code& err)
{
if (err)
{
disconnect();
}
else
{
async_read(socket_, boost::asio::buffer(user_),
boost::bind(&Connection::handle_user_read, this,
placeholders::error, placeholders::bytes_transferred));
}
}
void handle_user_read(const boost::system::error_code& err,
std::size_t bytes_transferred)
{
if (err)
{
disconnect();
}
else
{
...
}
}
...
void disconnect()
{
socket_.shutdown(tcp::socket::shutdown_both);
socket_.close();
socket_.open(tcp::v4());
start_accept();
}
tcp::acceptor &acceptor_;
tcp::socket socket_;
std::string user_;
std::string pass_;
...
};
Boost.Asio文档说明: 缓冲区对象将连续的内存区域表示为2元组,由指针和字节大小组成。形式为{void*,size_t}的元组指定一个可变(可修改)内存区域 这意味着,为了调用
async\u read
将数据写入缓冲区,它必须(在底层缓冲区对象中)是一个连续的内存块。此外,缓冲区对象必须能够写入该内存块
std::string
不允许对其缓冲区进行任意写入,因此async_read
无法将内存块写入字符串的缓冲区(请注意,std::string
确实允许调用方通过data()对底层缓冲区进行只读访问)
方法,该方法确保返回的指针在下次调用非常量成员函数之前有效。因此,Asio可以轻松创建一个const\u缓冲区
包装std::string
,您可以将其与async\u write
)一起使用
Asio文档中有一个简单的“聊天”程序的示例代码(请参阅),该程序有一个很好的解决此问题的方法。基本上,您首先需要让发送TCP沿着消息的大小发送,以“头”的形式进行排序,并且您的读取处理程序必须解释头以分配适合读取实际数据的固定大小的缓冲区
至于需要在async\u read
和async\u write
中使用shared\u from\u this()
,原因是它保证了由boost::bind
包装的方法将始终引用活动对象。考虑以下情况:
handle\u accept
方法调用async\u read
并将一个处理程序“发送到reactor”-基本上,您已经要求io\u服务在完成从套接字读取数据时调用Connection::handle\u user\u read
。io_服务
存储此函子并继续其循环,等待异步读取操作完成
async\u read
后,连接
对象由于某种原因(程序终止、错误条件等)被解除分配io_服务
现在确定异步读取已完成,在连接
对象被解除分配之后,但io_服务
被销毁之前(这可能发生,例如,如果io_服务::run
在单独的线程中运行,这是典型的情况)。现在,io_服务
尝试调用处理程序,它对连接
对象的引用无效shared_ptr
分配Connection
,并使用shared_from_this()
而不是this
将处理程序发送到“reactor”中,这允许io_服务
存储对对象的共享引用,而且shared\u ptr
保证在最后一次引用过期之前不会释放它
因此,您的代码应该看起来像:
class Connection : public boost::enable_shared_from_this<Connection>
{
public:
Connection(tcp::acceptor &acceptor) :
acceptor_(acceptor),
socket_(acceptor.get_io_service(), tcp::v4())
{ }
void start()
{
acceptor_.get_io_service().post(
boost::bind(&Connection::start_accept, shared_from_this()));
}
private:
void start_accept()
{
acceptor_.async_accept(socket_,
boost::bind(&Connection::handle_accept, shared_from_this(),
placeholders::error));
}
void handle_accept(const boost::system::error_code& err)
{
if (err)
{
disconnect();
}
else
{
async_read(socket_, boost::asio::buffer(user_),
boost::bind(&Connection::handle_user_read, shared_from_this(),
placeholders::error, placeholders::bytes_transferred));
}
}
//...
};
希望这有帮助 这并不是一个答案本身,只是一个冗长的注释:从ASIO缓冲区转换为字符串的一个非常简单的方法是从它流式传输:
asio::streambuf buff;
asio::read_until(source, buff, '\r'); // for example
istream is(&buff);
is >> targetstring;
当然,这是一个数据副本,但如果您想将其放入字符串中,则需要这样做。您可以将
std:string
与async\\u read()
一起使用,如下所示:
async_read(socket_, boost::asio::buffer(&user_[0], user_.size()),
boost::bind(&Connection::handle_user_read, this,
placeholders::error, placeholders::bytes_transferred));
但是,在调用async\\u read()
之前,最好确保std::string
足够大,可以接受您期望的数据包并用零填充
至于为什么如果可以删除对象,就不应该将成员函数回调绑定到
这个指针,可以在这里找到更完整的描述和更健壮的方法:。Boost Asio有两种类型的缓冲区。有boost::asio::buffer(您的\u数据\u结构)
,它不能增长,因此通常对未知输入无效,还有boost::asio::streambuf
可以增长
给定一个boost::asio::streambuf buf
,使用std::string(std::istreambuf_迭代器(&buf),{})将其转换为字符串代码>
这是没有效率的,因为您最终会再次复制数据,但这需要使boost::asio::buffer
意识到可增长的容器,即具有.resize(N)
方法的容器。如果不触碰Boost代码,就无法提高效率。这是正确答案。Asio可以读取许多缓冲区类型,也可以写入许多固定大小的缓冲区,但它将使用的唯一可变大小的缓冲区是Asio::streambuf
。正如MikeC正确指出的那样,这是一个数据副本,但是std::string
在增长时具有内部副本。请注意,提取方法操作符>>
是iostream中常用的空白解析方法。您还可以在istream上调用getline
async_read(socket_, boost::asio::buffer(&user_[0], user_.size()),
boost::bind(&Connection::handle_user_read, this,
placeholders::error, placeholders::bytes_transferred));