如何异步读取到std使用boost ::支持ASIO :: string的? [英] How to asynchronously read to std::string using Boost::asio?

查看:134
本文介绍了如何异步读取到std使用boost ::支持ASIO :: string的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我学习的boost :: ASIO和所有的东西异步。我怎么可以异步读取类型为std变量用户_ :: string的? 的boost :: ASIO ::缓​​冲区(用户_) async_write作品(),但不能与 async_read()。它与载体,那么什么是它不与字符串工作的原因吗?是否有另一种方式做到这一点,除了宣告字符用户_ [MAX_LEN] ,并使用的boost :: ASIO ::缓​​冲区(用户_,MAX_LEN)

此外,什么是从升压继承:: enable_shared_from_this&LT点;连接> ,并使用 shared_from_this()而不是的这个 async_read() async_write()?我已经看到了很多例子。

下面是我的code的一部分:

 类连接
{
    上市:        连接(TCP ::受体&放大器;受体):
            acceptor_(受体),
            socket_(acceptor.get_io_service(),TCP :: V4())
        {}        无效的start()
        {
            acceptor_.get_io_service()后(
                提高::绑定(安培;连接:: start_accept,这一点));
        }    私人的:        无效start_accept()
        {
            acceptor_.async_accept(socket_,
                提高::绑定(安培;连接:: handle_accept,为此,
                占位符::错误));
        }        无效handle_accept(常量的boost ::系统::错误_ code和;犯错)
        {
            如果(ERR)
            {
                断开();
            }
            其他
            {
                async_read(socket_,提高:: ASIO ::缓​​冲区(用户_),
                    提高::绑定(安培;连接:: handle_user_read,为此,
                    占位符::错误,占位符:: bytes_transferred));
            }
        }        无效handle_user_read(常量的boost ::系统::错误_ code和;错了,
            的std ::为size_t bytes_transferred)
        {
            如果(ERR)
            {
                断开();
            }
            其他
            {
                ...
            }
        }        ...        无效断开()
        {
            socket_.shutdown(TCP ::插座:: shutdown_both);
            socket_.close();
            socket_.open(TCP :: V4());
            start_accept();
        }        TCP ::受体和放大器; acceptor_;
        TCP ::插座socket_;
        标准::字符串用户_;
        标准::字符串pass_;
        ...
};


解决方案

该Boost.Asio的文档状态:


  

一个缓冲对象再presents一个连续的内存区域组成,以字节为指针和大小的2元组。表格{无效*,为size_t}的元组指定的内存可变(修改)区域。


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

的std :: string的不允许随意写入其缓冲区,所以 async_read 不能写的内存块成字符串的缓冲区(请注意,的std :: string的确实的通过给下面的缓冲来电只读访问数据()方法,它保证了返回的指针将是有效的,直到一个非const成员函数的一个电话。出于这个原因,短耳可以很容易地创建一个 const_buffer 包装的的std ::字符串,你可以用 async_write )。

该短耳文档有一个简单的聊天节目例如code(请参阅http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/examples.html#boost_asio.examples.chat)有解决这个问题的一个好方法。基本上,你需要有发送TCP沿着消息的大小发送首先,在各种各样的头,而你读的处理程序必须跨preT头分配固定大小的缓冲区适合于读出的实际数据。

至于需要使用 shared_from_this() async_read async_write ,其原因就在于它保证了通过的boost ::绑定总是引用现场的对象。考虑以下情况:


  1. handle_accept 的方法调用 async_read 并发送处理程序进入反应器 - 基本上你问 io_service对象调用连接:: handle_user_read 当它完成从插座中读取数据。在 io_service对象商店函子,并继续循环,等待异步读取操作来完成。

  2. 您调用 async_read 之后,连接对象被释放某种原因(程序结束,故障状态等)

  3. 假设 io_service对象现在确定异步读取完成后,的之后的的连接对象已经被释放,但的的的 io_service对象被破坏(这可能发生,例如,如果 io_service对象::运行在单独的线程中运行,为的是典型的)。现在, io_service对象试图调用处理程序,它有一个无效引用连接对象。

解决的办法是分配连接通过的shared_ptr ,并使用 shared_from_this()而不是这个发送处理程序进入反应器的时候 - 这使得 io_service对象存储一个共享的参考对象,而的shared_ptr 保证直到最后一个引用到期,便不会被释放。

所以,你的code或许应该是这​​个样子:

 类连接:公众的boost :: enable_shared_from_this<连接GT&;
{
上市:    连接(TCP ::受体&放大器;受体):
        acceptor_(受体),
        socket_(acceptor.get_io_service(),TCP :: V4())
    {}    无效的start()
    {
        acceptor_.get_io_service()后(
            提高::绑定(安培;连接:: start_accept,shared_from_this()));
    }私人的:    无效start_accept()
    {
        acceptor_.async_accept(socket_,
            提高::绑定(安培;连接:: handle_accept,shared_from_this()
            占位符::错误));
    }    无效handle_accept(常量的boost ::系统::错误_ code和;犯错)
    {
        如果(ERR)
        {
            断开();
        }
        其他
        {
            async_read(socket_,提高:: ASIO ::缓​​冲区(用户_),
                提高::绑定(安培;连接:: handle_user_read,shared_from_this()
                占位符::错误,占位符:: bytes_transferred));
        }
    }
    // ...
};

请注意,你现在必须确保每个连接对象是通过的shared_ptr ,例如分配的:

 的boost :: shared_ptr的<连接GT&; new_conn(新连接(...));

希望这有助于!

I'm learning Boost::asio and all that async stuff. How can I asynchronously read to variable user_ of type std::string? Boost::asio::buffer(user_) works only with async_write(), but not with async_read(). It works with vector, so what is the reason for it not to work with string? Is there another way to do that besides declaring char user_[max_len] and using Boost::asio::buffer(user_, max_len)?

Also, what's the point of inheriting from boost::enable_shared_from_this<Connection> and using shared_from_this() instead of this in async_read() and async_write()? I've seen that a lot in the examples.

Here is a part of my code:

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_;
        ...
};

解决方案

The Boost.Asio documentation states:

A buffer object represents a contiguous region of memory as a 2-tuple consisting of a pointer and size in bytes. A tuple of the form {void*, size_t} specifies a mutable (modifiable) region of memory.

This means that in order for a call to async_read to write data to a buffer, it must be (in the underlying buffer object) a contiguous block of memory. Additionally, the buffer object must be able to write to that block of memory.

std::string does not allow arbitrary writes into its buffer, so async_read cannot write chunks of memory into a string's buffer (note that std::string does give the caller read-only access to the underlying buffer via the data() method, which guarantees that the returned pointer will be valid until the next call to a non-const member function. For this reason, Asio can easily create a const_buffer wrapping an std::string, and you can use it with async_write).

The Asio documentation has example code for a simple "chat" program (see http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/examples.html#boost_asio.examples.chat) that has a good method of overcoming this problem. Basically, you need to have the sending TCP send along the size of a message first, in a "header" of sorts, and your read handler must interpret the header to allocate a buffer of a fixed size suitable for reading the actual data.

As far as the need for using shared_from_this() in async_read and async_write, the reason is that it guarantees that the method wrapped by boost::bind will always refer to a live object. Consider the following situation:

  1. Your handle_accept method calls async_read and sends a handler "into the reactor" - basically you've asked the io_service to invoke Connection::handle_user_read when it finishes reading data from the socket. The io_service stores this functor and continues its loop, waiting for the asynchronous read operation to complete.
  2. After your call to async_read, the Connection object is deallocated for some reason (program termination, an error condition, etc.)
  3. Suppose the io_service now determines that the asynchronous read is complete, after the Connection object has been deallocated but before the io_service is destroyed (this can occur, for example, if io_service::run is running in a separate thread, as is typical). Now, the io_service attempts to invoke the handler, and it has an invalid reference to a Connection object.

The solution is to allocate Connection via a shared_ptr and use shared_from_this() instead of this when sending a handler "into the reactor" - this allows io_service to store a shared reference to the object, and shared_ptr guarantees that it won't be deallocated until the last reference expires.

So, your code should probably look something like:

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));
        }
    }
    //...
};

Note that you now must make sure that each Connection object is allocated via a shared_ptr, e.g.:

boost::shared_ptr<Connection> new_conn(new Connection(...));

Hope this helps!

这篇关于如何异步读取到std使用boost ::支持ASIO :: string的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆