在C++ 11中重复std :: move on boost :: asio socket对象

Kev*_*son 24 boost move boost-asio move-semantics c++11

我正在探索使用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成员socket_的的server类.在类的do_accept()方法中server,socket_传递给async_accept().(根据asio文档,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;
} 
Run Code Online (Sandbox Code Playgroud)

Igo*_* R. 21

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

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

这些数据意味着,你可以move在原来的socket从对象serversession,因为你需要尽可能多的时间.

  • 还有一个想法......这不是混淆代码吗?这是否不会暴露应该更明确的隐藏功能? (9认同)
  • @Igor R.嗯,我的理解是,使用移动语义,(源)rvalue应该处于准备好销毁的状态.这里不太明显的事情(对我来说)是rvalue会被重新初始化,好像它是我们移动后的新构造的对象.当然代码是干净的,但知道这个结果是(imho)`session`构造函数的实现细节,不是吗?否则,我认为我们通常不应该在它移动到其他地方之后尝试使用(或重新使用)右值,对吧? (3认同)

dem*_*nes 6

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

在这种情况下,object(server::socket_)是从OS套接字资源的所有权转移的接收者server::acceptor_.async_accept()当客户端连接时,在返回后的某个时刻发生转移.将新连接的套接字资源移入socket_,并调用回调lambda函数.在lambda期间,套接字资源被移入session::socket_.Server :: socket_仅拥有该资源一小部分.

移动语义允许RAII类存在于不拥有任何资源的暮光状态.想想unique_ptr释放后的呼叫(它指的是没有记忆).移出后的server :: socket_仍然有空间来容纳资源,但目前它什么也没有.

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