我正在学习Boost :: asio和所有异步的东西。如何异步读取std :: string类型的变量user_
? Boost::asio::buffer(user_)
仅适用于async_write()
,但不适用于async_read()
。它适用于矢量,那么它不使用字符串的原因是什么?除了声明char user_[max_len]
并使用Boost::asio::buffer(user_, max_len)
?
此外,在boost::enable_shared_from_this<Connection>
和shared_from_this()
中继承this
并使用async_read()
代替async_write()
的重点是什么?我在例子中看到了很多。
以下是我的代码的一部分:
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_;
...
};
答案 0 :(得分:30)
Boost.Asio文档声明:
缓冲区对象将连续的内存区域表示为2元组,由指针和大小(以字节为单位)组成。形式为{void *,size_t}的元组指定了一个可变(可修改)的内存区域。
这意味着为了调用async_read
将数据写入缓冲区,它必须(在底层缓冲区对象中)一个连续的内存块。此外,缓冲区对象必须能够写入该内存块。
std::string
不允许任意写入其缓冲区,因此async_read
无法将内存块写入字符串的缓冲区(请注意std::string
给出调用者通过data()
方法对底层缓冲区进行只读访问,这样可以保证返回的指针在下次调用非const成员函数之前一直有效。因此,Asio可以轻松创建一个{{ 1}}包裹const_buffer
,您可以将其与std::string
)一起使用。
Asio文档包含一个简单的“聊天”程序的示例代码(请参阅http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/examples.html#boost_asio.examples.chat),该程序具有克服此问题的良好方法。基本上,您需要首先按照消息的大小发送TCP,然后读取处理程序必须解释标头以分配适合于读取实际数据的固定大小的缓冲区。 / p>
至于在async_write
和shared_from_this()
中使用async_read
的需求,原因是它保证async_write
包裹的方法始终引用直播宾语。请考虑以下情况:
boost::bind
方法调用handle_accept
并将处理程序“发送到反应堆中” - 基本上您已经要求async_read
在完成从io_service
读取数据时调用Connection::handle_user_read
插座。 io_service
存储此仿函数并继续循环,等待异步读取操作完成。async_read
后,Connection
对象因某种原因(程序终止,错误情况等)被解除分配。io_service
现在确定异步读取已完成, Connection
对象已被解除分配但之前 {{1>被销毁(例如,如果io_service
在一个单独的线程中运行,就会发生这种情况,这是典型的)。现在,io_service::run
尝试调用处理程序,并且它对io_service
对象的引用无效。解决方案是通过Connection
分配Connection
,并在将“处理程序”“发送到反应堆”时使用shared_ptr
代替shared_from_this()
- 这允许{{1}存储对象的共享引用,this
保证在最后一个引用到期之前不会释放它。
因此,您的代码应该类似于:
io_service
请注意,您现在必须确保通过shared_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));
}
}
//...
};
对象,例如:
Connection
希望这有帮助!
答案 1 :(得分:9)
这本身并不是一个答案,而只是一个冗长的评论:从ASIO缓冲区转换为字符串的一种非常简单的方法是从它流式传输:
asio::streambuf buff;
asio::read_until(source, buff, '\r'); // for example
istream is(&buff);
is >> targetstring;
当然,这是一个数据副本,但如果你想要它在字符串中,那就是你需要做的。
答案 2 :(得分:4)
您可以std:string
使用async\_read()
,如下所示:
async_read(socket_, boost::asio::buffer(&user_[0], user_.size()),
boost::bind(&Connection::handle_user_read, this,
placeholders::error, placeholders::bytes_transferred));
但是,您最好确保std::string
足够大,可以接受您期望的数据包,并在调用async\_read()
之前用零填充。
至于为什么你应该从不将成员函数回调绑定到this
指针,如果可以删除对象,可以在这里找到更完整的描述和更健壮的方法:Boost async_* functions and shared_ptr's。
答案 3 :(得分:1)
Boost Asio有两种缓冲区。那些boost::asio::buffer(your_data_structure)
,不能成长,因此对于未知输入通常无用,并且可以boost::asio::streambuf >成长。
给定boost::asio::streambuf buf
,您可以将其变为带有std::string(std::istreambuf_iterator<char>(&buf), {});
的字符串。
当您最终再次复制数据时效率不高,但这需要让boost::asio::buffer
知道可扩展容器,即具有.resize(N)
方法的容器。如果不触及Boost代码,您就无法提高效率。