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