bibliotecas boost c++11 boost-asio move move-semantics

bibliotecas - boost c++



Repetido std:: move en un objeto boost:: asio socket en C++ 11 (2)

Como se documenta en tcp::socket reference :

Después del movimiento, el objeto movido desde está en el mismo estado que si se construyera usando el constructor basic_stream_socket (io_service &).

Lo anterior significa que puede move el objeto socket original del server a la session tantas veces como lo necesite.

Estoy explorando el uso de boost :: asio junto con las características de C ++ 11. En particular, me estoy enfocando en un ejemplo llamado "async_tcp_echo_server.cpp", que se encuentra aquí (el código también se muestra al final de mi pregunta):

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

Mi pregunta involucra el tcp::socket socket_ de la clase del server . En el método do_accept() de la clase de server , socket_ pasa a async_accept() . (De acuerdo con la documentación de asio, async_accept() requiere, como primer parámetro, el socket para aceptar la conexión.) Hasta ahora, todo bien.

El siguiente parámetro, la devolución de llamada para la operación de aceptación asincrónica, es una función lambda. El cuerpo de la lambda construye un nuevo objeto de session , cuyo constructor también necesita el mismo socket . Curiosamente, los objetos de socket no se pueden copiar; entonces en el ejemplo, el objeto socket_ , que es un miembro del objeto del server , se pasa usando std::move() .

Entiendo que el objeto "único" socket_ (que es un miembro "permanente" del objeto del server ) se "mueve" al objeto de la session . El objeto Fine- socket no se copia, sino que se mueve, todo el mundo está contento.

Pero, ¿qué sucede en la próxima llamada a async_accept() ? ¿El mismo socket_ (miembro del server ), que se movió previamente, pasó nuevamente? Cuando "movemos" a un miembro, ¿qué queda? ¿Hay una fuente mágica de objetos de socket ilimitados?

¿O algo realmente menos que obvio está sucediendo aquí? Cuando el socket se mueve a la session , ¿se intercambia el contenido del objeto "left behind / moved from" ( socket_ member del server ) con el contenido del miembro "nuevo" session objeto de session "todavía no construido" socket_ member? ¿Tengo incluso sentido?

Resumen

El código está abajo. El flujo del programa es bastante simple. main() construye un solo objeto de server . El server realiza llamadas repetidas a async_accept() . Cada devolución de llamada async_accept() crea un nuevo objeto de session , cada uno construido con un socket (¿nuevo?). ¿De dónde vienen todos los objetos de socket "frescos", si simplemente se "mueven" (repetidamente) del mismo miembro de socket_ en el server (único)?

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


Se puede considerar que la semántica de movimiento es propiedad de los recursos. La Adquisición de recursos es una instanciación (RAII) es el concepto de asignar la propiedad de los recursos en el momento de la construcción del objeto y la liberación de esos recursos en la destrucción. La semántica de movimiento permite la transferencia de propiedad de los recursos en otros momentos además de la construcción y la destrucción.

En este caso, el objeto (servidor :: socket_) es el destinatario de una transferencia de propiedad del recurso de socket del sistema operativo desde el servidor :: acceptor_. Esa transferencia ocurre en algún momento después de que devuelve async_accept (), cuando un cliente se conecta. Los recursos de socket recién conectados se mueven a socket_ y se llama a la función callback lambda. Durante el lambda, los recursos del socket se mueven a session :: socket_. Servidor :: socket_ solo poseía el recurso por una fracción de un microsegundo.

La semántica de movimiento permite que las clases de RIAA existan en el estado crepuscular de no tener ningún recurso. Piense en un unique_ptr después de una llamada para lanzar. (No hace referencia a la memoria.) El servidor :: socket_ después de la mudanza todavía tiene espacio para contener un recurso, pero por el momento no posee nada.

Lo último que hace la función lambda es llamar a do_accept, que llama a async_accept () nuevamente. Se transfiere una referencia a socket_. Cuando otro cliente se conecta en algún momento en el futuro, async_accept () transferirá allí la propiedad de un socket del SO recién conectado.