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

Spy*_*Bot 13 string boost asynchronous boost-asio

我正在学习Boost :: asio和所有异步的东西.如何异步读取user_std :: string类型的变量?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()而不是thisin 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_;
        ...
};
Run Code Online (Sandbox Code Playgroud)

bjl*_*aub 31

Boost.Asio文档说明:

缓冲区对象将连续的内存区域表示为2元组,由指针和字节大小组成.形式为{void*,size_t}的元组指定可变(可修改)的内存区域.

这意味着为了调用async_read将数据写入缓冲区,它必须(在底层缓冲区对象中)是一块连续的内存块.此外,缓冲区对象必须能够写入该内存块.

std::string不允许任意写入其缓冲区,因此async_read不能将内存块写入字符串的缓冲区(注意,std::string 它确实通过该data()方法给调用者只读访问底层缓冲区,这保证了返回的指针在下一次有效之前有效调用非const成员函数.因此,Asio可以轻松创建const_buffer包装std::string,并且可以使用它async_write.

Asio文档包含一个简单的"聊天"程序的示例代码(请参阅http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/examples.html#boost_asio.examples.chat)克服这个问题的好方法.基本上,您需要首先在消息的大小中发送TCP,然后您的读取处理程序必须解释标头以分配适合于读取实际数据的固定大小的缓冲区.

至于需要使用shared_from_this()async_readasync_write,原因是它保证了被缠绕方法boost::bind将总是指活的对象.考虑以下情况:

  1. 你的handle_accept方法调用async_read并将一个处理程序"发送到反应堆" - 基本上你已经要求它在完成从套接字读取数据时io_service调用Connection::handle_user_read.该io_service存储这个函子,并继续其循环,等待异步读取操作完成.
  2. 在您调用之后async_read,由于Connection某种原因(程序终止,错误条件等)取消分配对象
  3. 假设io_service现在确定异步读取完成时,之后Connection对象已被解除分配,但之前io_service被破坏(这可能发生,例如,如果io_service::run在一个单独的线程运行时,由于是典型值).现在,io_service尝试调用处理程序,它对Connection对象的引用无效.

解决方案是Connection通过a shared_ptr和use 分配shared_from_this()而不是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));
        }
    }
    //...
};
Run Code Online (Sandbox Code Playgroud)

请注意,您现在必须确保Connection通过a分配每个对象shared_ptr,例如:

boost::shared_ptr<Connection> new_conn(new Connection(...));
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!


Mik*_*e C 10

这本身并不是一个答案,而只是一个冗长的注释:从ASIO缓冲区转换为字符串的一种非常简单的方法是从它流式传输:

asio::streambuf buff;
asio::read_until(source, buff, '\r');  // for example

istream is(&buff);
is >> targetstring;
Run Code Online (Sandbox Code Playgroud)

当然,这是一个数据副本,但如果你想要它在字符串中,那就是你需要做的.

  • 这是正确的答案。Boost.Asio 可以从许多缓冲区类型中读取数据,并写入许多固定大小的缓冲区,但“asio::streambuf”是它唯一可以使用的可变大小缓冲区。正如 MikeC 正确指出的那样,这是一个数据副本,但“std::string”无论如何增长时都会有内部副本。请注意,提取方法“operator&gt;&gt;”是来自 iostream 的常用空白解析方法。您还可以在“istream”上调用“getline”。 (2认同)

ken*_*nba 5

您可以像这样使用std:stringwith async\_read()

async_read(socket_, boost::asio::buffer(&user_[0], user_.size()),
           boost::bind(&Connection::handle_user_read, this,
           placeholders::error, placeholders::bytes_transferred));
Run Code Online (Sandbox Code Playgroud)

但是,您最好确保std::string在调用async\_read().

至于为什么如果可以删除对象,则永远不要将成员函数回调绑定到this指针,可以在此处找到更完整的描述和更健壮的方法:Boost async_* functions and shared_ptr's