Skip to main content
 首页 » 编程设计

boost之在 C++11 中的 boost::asio 套接字对象上重复 std::move

2024年12月31日17lexus

我正在探索使用 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成员(member)socket_server类(class)。在 do_accept() server的方法类(class),socket_传递给 async_accept() . (根据 asio 文档,async_accept() 需要作为其第一个参数的 socket 来接受连接。)到目前为止,一切都很好。

下一个参数,异步接受操作的回调,是一个 lambda 函数。 lambda 的主体构造了一个新的 session对象,其构造函数也需要相同的 socket .有趣的是,socket对象不能被复制;所以在这个例子中,socket_对象,它是 server 的成员对象,使用 std::move() 传递.

据我所知,“唯一”socket_对象(它是 server 对象的“永久”成员)被“move ”到 session 中目的。很好 -- socket对象不是复制,而是 move ——每个人都很高兴。

但是下次拨打 async_accept() 会发生什么? ?是不是一样socket_ (server的成员),之前搬过的,又传进来了?当我们“move ”一个成员时,留下了什么?有没有无限魔力的喷泉socket对象?

或者这里真的发生了一些不太明显的事情?当socket移入 session , 是“遗留/移出”对象( socket_ server 的成员)的内容与"new" session 的内容交换对象自己的“尚未构建”socket_成员?我还有道理吗?

概括

代码如下。程序流程相当简单。 main()构造单个 server目的。 server重复拨打 async_accept() .每个async_accept()回调创建一个新的 session对象,每个对象都用一个(新鲜的?)socket 构造.哪里来的“新鲜”socket对象来自,如果它们只是(重复)从同一个 socket_“move ”成员(member)(单例)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; 
}  

请您参考如下方法:

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

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

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

lambda 函数所做的最后一件事是调用 do_accept , 调用 async_accept()再次。引用 socket_传入。当另一个客户端在 future 某个时刻连接时,async_accept()将在那里转移新连接的操作系统套接字的所有权。