Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Repeated std::move on an boost::asio socket object in C++11

Tags:

I am exploring using boost::asio along with C++11 features. In particular, I am focusing on an example called "async_tcp_echo_server.cpp", located here (code is also shown at the end of my question):

http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/example/cpp11/echo/async_tcp_echo_server.cpp

My question involves the tcp::socket member socket_ of the server class. In the do_accept() method of the server class, socket_ is passed to async_accept(). (According to the asio documentation, async_accept() requires, as its first parameter, the socket to accept the connection into.) So far, so good.

The next parameter, the callback for the asynchronous accept operation, is a lambda function. The body of the lambda constructs a new session object, whose constructor also needs the same socket. Interestingly, socket objects cannot be copied; so in the example, the socket_ object, which is a member of the server object, is passed using std::move().

I understand that the "one and only" socket_ object (which is a "permanent" member of the server object) is "moved" into the session object. Fine -- socket object is not copied, but moved -- everybody's happy.

But what happens on the next call to async_accept()? Is the same socket_ (member of server), that was previously moved, passed in again? When we "move" a member, what is left behind? Is there a magical fountain of unlimited socket objects?

Or is something really less-than-obvious happening here? When the socket is moved into the session, is the contents of the "left behind/moved from" object (socket_ member of server) swapped with the contents of the "new" session object's own "not-yet-constructed" socket_ member? Am I even making sense?

Summary

Code is below. Program flow is fairly simple. main() constructs a single server object. The server makes repeated calls to async_accept(). Each async_accept() callback creates a new session object, each constructed with a (fresh?) socket. Where do all the "fresh" socket objects come from, if they are simply (repeatedly) "moved" from the same socket_ member in the (single) server?

#include <cstdlib> #include <iostream> #include <memory> #include <utility> #include <boost/asio.hpp>  using boost::asio::ip::tcp;  class session : public std::enable_shared_from_this<session> { public:     session( tcp::socket socket )     : socket_( std::move( socket ) )     {}      void start() {         do_read();     }  private:     void do_read() {         auto self( shared_from_this() );         socket_.async_read_some(             boost::asio::buffer( data_, max_length ),             [this, self]( boost::system::error_code ec, std::size_t length )             {                 if( !ec ) {                     do_write( length );                 }             }         );     }      void do_write( std::size_t length ) {         auto self( shared_from_this() );         boost::asio::async_write(             socket_,             boost::asio::buffer( data_, length ),             [this, self]( boost::system::error_code ec, std::size_t /*length*/ )             {                 if( !ec ) {                     do_read();                 }             }         );     }      tcp::socket socket_;     enum { max_length = 1024 };     char data_[max_length]; };   class server { public:     server( boost::asio::io_service& io_service, short port )     : acceptor_( io_service, tcp::endpoint( tcp::v4(), port ) )     , socket_( io_service )     {         do_accept();     }  private:     void do_accept() {         acceptor_.async_accept(             socket_,             [this]( boost::system::error_code ec )             {                if( !ec ) {                    std::make_shared<session>( std::move( socket_ ) )->start();  // is this a *swap* of socket_ ???                }                 do_accept();             }         );     }      tcp::acceptor acceptor_;     tcp::socket socket_; };   int main( int argc, char* argv[] ) {     try {         if( argc != 2 ) {             std::cerr << "Usage: async_tcp_echo_server <port>\n";             return 1;         }          boost::asio::io_service io_service;          server s( io_service, std::atoi( argv[1] ) );          io_service.run();      } catch( std::exception& e ) {         std::cerr << "Exception: " << e.what() << "\n";     }      return 0; }  
like image 387
Kevin H. Patterson Avatar asked Jul 18 '13 06:07

Kevin H. Patterson


1 Answers

As documented in tcp::socket reference:

Following the move, the moved-from object is in the same state as if constructed using the basic_stream_socket(io_service&) constructor.

The above means that you can move the original socket object from server to session as many times as you need.

like image 190
Igor R. Avatar answered Sep 21 '22 12:09

Igor R.