24

C++ 11 の機能と共に boost::asio を使用して調査しています。特に、ここにある「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_に関係しています。クラスserverdo_accept()メソッドでは、に渡されます。(asio のドキュメントによると、最初のパラメーターとして、接続を受け入れるには が必要です。) これまでのところ、とても良いです。serversocket_async_accept()async_accept()socket

次のパラメーターである非同期受け入れ操作のコールバックは、ラムダ関数です。ラムダの本体は新しいsessionオブジェクトを構築し、そのコンストラクターも同じ を必要としsocketます。興味深いことに、socketオブジェクトはコピーできません。そのため、この例では、socket_オブジェクトのメンバーであるserverオブジェクトが を使用して渡されstd::move()ます。

「唯一の」socket_オブジェクト (オブジェクトの「永続的な」メンバーserver) がオブジェクトに「移動」されることを理解していsessionます。結構です --socketオブジェクトはコピーされませんが、移動されます -- みんな幸せです。

しかし、次に を呼び出すとどうなるasync_accept()でしょうか。以前に移動された同じsocket_(のメンバーserver) が再度渡されましたか? メンバーを「移動」すると、何が残されますか? socket無制限のオブジェクトの魔法の泉はありますか?

それとも、ここで実際に明らかではない何かが起こっているのでしょうか? がsocketに移動されるsessionと、「取り残された/移動された」オブジェクト (socket_のメンバーserver)の内容は、「新しい」オブジェクト自体の「まだ構築されていない」メンバーの内容と交換されますか? 私は理にかなっていますか?sessionsocket_

概要

コードは以下です。プログラムの流れはかなり単純です。main()単一のserverオブジェクトを構築します。はserverを繰り返し呼び出しますasync_accept()。各async_accept()コールバックは新しいsessionオブジェクトを作成し、それぞれが (fresh?) で構築されますsocket。(single)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;
} 
4

2 に答える 2

6

移動セマンティクスは、リソースの所有権を渡すことと考えることができます。Resource Acquisition Is Instantiation (RAII) は、オブジェクトの構築時にリソースの所有権を割り当て、破棄時にそれらのリソースを解放するという概念です。移動セマンティクスにより、構築と破棄以外の時点でリソースの所有権を譲渡できます。

この場合、オブジェクト ( server::socket_) は、 からの OS ソケット リソースの所有権の譲渡の受信者ですserver::acceptor_async_accept()その転送は、クライアントが接続したときに、返された後のある時点で発生します。新しく接続されたソケット リソースが に移動されsocket_、コールバック ラムダ関数が呼び出されます。ラムダの間、ソケット リソースは に移動されsession::socket_ます。Server::socket_ がリソースを所有していたのは、わずか 1 マイクロ秒だけでした。

ムーブ セマンティクスにより、RAII クラスは、リソースを所有していない薄明かりの状態で存在できます。release の呼び出しの後の aを考えてくださいunique_ptr(メモリがないことを示します)。移動後の server::socket_ にはリソースを保持するスペースがまだありますが、現時点では何も所有していません。

ラムダ関数が最後に行うのは calldo_acceptで、これがasync_accept()再度呼び出します。への参照socket_が渡されます。将来のある時点で別のクライアントが接続すると、async_accept()そこに新たに接続された OS ソケットの所有権が転送されます。

于 2014-05-14T06:57:22.193 に答える