如何使用Boost :: asio异步读取到std :: string?

时间:2010-05-07 17:23:01

标签: string boost asynchronous boost-asio

我正在学习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_;
        ...
};

4 个答案:

答案 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_writeshared_from_this()中使用async_read的需求,原因是它保证async_write包裹的方法始终引用直播宾语。请考虑以下情况:

  1. 您的boost::bind方法调用handle_accept并将处理程序“发送到反应堆中” - 基本上您已经要求async_read在完成从io_service读取数据时调用Connection::handle_user_read插座。 io_service存储此仿函数并继续循环,等待异步读取操作完成。
  2. 致电async_read后,Connection对象因某种原因(程序终止,错误情况等)被解除分配。
  3. 假设io_service现在确定异步读取已完成, Connection对象已被解除分配但之前 {{1>被销毁(例如,如果io_service在一个单独的线程中运行,就会发生这种情况,这是典型的)。现在,io_service::run尝试调用处理程序,并且它对io_service对象的引用无效。
  4. 解决方案是通过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代码,您就无法提高效率。