Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The Boost.Asio documentation states:</p> <blockquote> <p>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.</p> </blockquote> <p>This means that in order for a call to <code>async_read</code> 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. </p> <p><code>std::string</code> does not allow arbitrary writes into its buffer, so <code>async_read</code> cannot write chunks of memory into a string's buffer (note that <code>std::string</code> <em>does</em> give the caller read-only access to the underlying buffer via the <code>data()</code> 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 <code>const_buffer</code> wrapping an <code>std::string</code>, and you can use it with <code>async_write</code>).</p> <p>The Asio documentation has example code for a simple "chat" program (see <a href="http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/examples.html#boost_asio.examples.chat" rel="noreferrer">http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/examples.html#boost_asio.examples.chat</a>) 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.</p> <p>As far as the need for using <code>shared_from_this()</code> in <code>async_read</code> and <code>async_write</code>, the reason is that it guarantees that the method wrapped by <code>boost::bind</code> will always refer to a live object. Consider the following situation:</p> <ol> <li>Your <code>handle_accept</code> method calls <code>async_read</code> and sends a handler "into the reactor" - basically you've asked the <code>io_service</code> to invoke <code>Connection::handle_user_read</code> when it finishes reading data from the socket. The <code>io_service</code> stores this functor and continues its loop, waiting for the asynchronous read operation to complete.</li> <li>After your call to <code>async_read</code>, the <code>Connection</code> object is deallocated for some reason (program termination, an error condition, etc.)</li> <li>Suppose the <code>io_service</code> now determines that the asynchronous read is complete, <em>after</em> the <code>Connection</code> object has been deallocated but <em>before</em> the <code>io_service</code> is destroyed (this can occur, for example, if <code>io_service::run</code> is running in a separate thread, as is typical). Now, the <code>io_service</code> attempts to invoke the handler, and it has an invalid reference to a <code>Connection</code> object.</li> </ol> <p>The solution is to allocate <code>Connection</code> via a <code>shared_ptr</code> and use <code>shared_from_this()</code> instead of <code>this</code> when sending a handler "into the reactor" - this allows <code>io_service</code> to store a shared reference to the object, and <code>shared_ptr</code> guarantees that it won't be deallocated until the last reference expires.</p> <p>So, your code should probably look something like:</p> <pre><code>class Connection : public boost::enable_shared_from_this&lt;Connection&gt; { public: Connection(tcp::acceptor &amp;acceptor) : acceptor_(acceptor), socket_(acceptor.get_io_service(), tcp::v4()) { } void start() { acceptor_.get_io_service().post( boost::bind(&amp;Connection::start_accept, shared_from_this())); } private: void start_accept() { acceptor_.async_accept(socket_, boost::bind(&amp;Connection::handle_accept, shared_from_this(), placeholders::error)); } void handle_accept(const boost::system::error_code&amp; err) { if (err) { disconnect(); } else { async_read(socket_, boost::asio::buffer(user_), boost::bind(&amp;Connection::handle_user_read, shared_from_this(), placeholders::error, placeholders::bytes_transferred)); } } //... }; </code></pre> <p>Note that you now must make sure that each <code>Connection</code> object is allocated via a <code>shared_ptr</code>, e.g.:</p> <pre><code>boost::shared_ptr&lt;Connection&gt; new_conn(new Connection(...)); </code></pre> <p>Hope this helps!</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload