Я изучаю использование boost:: asio вместе с функциями С++ 11. В частности, я сосредоточен на приведенном здесь примере async_tcp_echo_server.cpp(код также показан в конце моего вопроса):
В моем вопросе участвует член tcp::socket
socket_
класса server
. В методе do_accept()
класса server
socket_
передается async_accept()
. (Согласно документации asio, async_accept()
в качестве своего первого параметра требуется socket
принять соединение.) До сих пор так хорошо.
Следующий параметр, обратный вызов для операции асинхронного приема, является лямбда-функцией. Тело лямбда конструирует новый объект session
, конструктор которого также нуждается в том же socket
. Интересно, что объекты socket
не могут быть скопированы; поэтому в примере объект socket_
, являющийся членом объекта server
, передается с помощью std::move()
.
Я понимаю, что объект "единственный и единственный" socket_
(который является "постоянным" членом объекта server
) "перемещается" в объект session
. Fine - socket
объект не копируется, а перемещается - все счастливы.
Но что происходит при следующем вызове async_accept()
? Является ли тот же socket_
(член server
), который был ранее перемещен, снова принят? Когда мы "перемещаем" участника, что осталось? Есть ли волшебный фонтан неограниченных объектов socket
?
Или что-то действительно менее-очевидное происходит здесь? Когда socket
перемещается в session
, содержимое объекта "left behind/move from" (socket_
member of server
) заменяется содержимым "нового" session
объекта собственного "еще не построенный" socket_
участник? Я даже чувствую смысл?
Резюме
Код ниже. Поток программ довольно прост. main()
создает один объект server
. server
выполняет повторные вызовы async_accept()
. Каждый обратный вызов async_accept()
создает новый объект session
, каждый из которых сконструирован с помощью (fresh?) socket
. Откуда берутся все "свежие" объекты socket
, если они просто (неоднократно) "перемещаются" из одного и того же члена socket_
в (одном) 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;
}