24

我正在探索使用 boost::asio 和 C++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成员。在类的方法中,传递给. (根据 asio 文档,需要作为它的第一个参数来接受连接。)到目前为止,一切都很好。socket_serverdo_accept()serversocket_async_accept()async_accept()socket

下一个参数,异步接受操作的回调,是一个 lambda 函数。lambda 的主体构造了一个新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。如果它们只是(重复地)从(单个)中的同一成员“移动”,那么所有“新鲜”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

21

tcp::socket参考文献中所述:

在移动之后,被移动的对象处于与使用 basic_stream_socket(io_service&) 构造函数构造时相同的状态。

以上意味着您可以根据需要move将原始socket对象从serversession多次。

于 2013-07-18T12:08:46.843 回答
6

移动语义可以被认为是传递资源的所有权。资源获取即实例化 (RAII) 是在对象构造时分配资源所有权并在销毁时释放这些资源的概念。移动语义允许在构建和销毁之外的其他时间转移资源的所有权。

在这种情况下,对象 ( server::socket_) 是来自 的 OS 套接字资源所有权转移的接收者server::acceptor_async_accept()当客户端连接时,该传输发生在返回后的某个时间点。新连接的socket资源被移入socket_,回调lambda函数被调用。在 lambda 期间,套接字资源被移动到session::socket_. Server::socket_ 只拥有资源的时间只有几分之一微秒。

移动语义允许 RAII 类以不拥有任何资源的暮光状态存在。在调用释放后想一想unique_ptr(它指的是没有内存)。移出后的 server::socket_ 仍有空间容纳资源,但目前它什么都不拥有。

lambda 函数做的最后一件事是 call do_accept,它async_accept()再次调用。传入的引用socket_。当另一个客户端在未来某个时间点连接时,async_accept()将在那里转移新连接的 OS 套接字的所有权。

于 2014-05-14T06:57:22.193 回答