Повторный std:: move on boost:: asio socket object в С++ 11 - программирование
Подтвердить что ты не робот

Повторный std:: move on boost:: asio socket object в С++ 11

Я изучаю использование boost:: asio вместе с функциями С++ 11. В частности, я сосредоточен на приведенном здесь примере async_tcp_echo_server.cpp(код также показан в конце моего вопроса):

http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/example/cpp11/echo/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;
} 
4b9b3361

Ответ 1

Перемещение семантики можно рассматривать как передачу владения ресурсами. Приобретение ресурсов - это активация (RAII) - это концепция назначения права собственности на ресурсы во время строительства объекта и освобождения этих ресурсов при уничтожении. Перемещение семантики допускает передачу права собственности на ресурсы в другое время, кроме строительства и уничтожения.

В этом случае объект (server:: socket_) является получателем передачи права собственности на ресурс сокета ОС с сервера:: acceptor_. Эта передача происходит в некоторый момент после возврата async_accept(), когда клиент подключается. Недавно подключенные ресурсы сокетов перемещаются в socket_, и вызывается функция лямбда-вызова обратного вызова. Во время лямбда ресурсы сокета перемещаются в session:: socket_. Сервер:: socket_ только принадлежал ресурсу на долю микросекунды.

Перемещение семантики позволяет классам RIAA существовать в сумеречном состоянии, не владея любыми ресурсами. Подумайте о уникальном_ptr после вызова для выпуска. (Это означает, что нет памяти.) Сервер:: socket_ после выхода еще имеет место для хранения ресурса, но на данный момент ему ничего не принадлежит.

Последнее, что делает лямбда-функция, - вызов do_accept, который снова вызывает async_accept(). Ссылка на socket_ передается. Когда другой клиент будет подключаться в какой-то момент в будущем, async_accept() передаст право собственности на вновь подключенный к нему сокет ОС.

Ответ 2

Как описано в tcp::socket ссылке:

После перемещения перемещенный объект находится в том же состоянии, что и построенный с использованием конструктора basic_stream_socket (io_service &).

Вышеупомянутое означает, что вы можете move исходный socket объект от server до session столько раз, сколько вам нужно.