我正在探索使用boost :: asio以及C ++ 11功能。特别是,我专注于一个名为“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
)的内容是否与“新”session
对象自己的“尚未构建的”socket_
成员的内容交换?我甚至有意义吗?
代码如下。程序流程相当简单。 main()
构造一个server
对象。 server
反复打电话给async_accept()
。每个async_accept()
回调创建一个新的session
对象,每个对象用(新鲜?)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;
}
移动语义可以被认为是通过资源的所有权。资源获取实例化(RAII)是在对象构建时分配资源所有权以及在销毁时释放这些资源的概念。移动语义允许在构造和销毁之外的其他时间转移资源的所有权。
在这种情况下,对象(server::socket_
)是从server::acceptor_
转移OS套接字资源的所有权的接收者。当客户连接时,async_accept()
返回后的某个时刻发生转移。新连接的套接字资源被移动到socket_
,并调用回调lambda函数。在lambda期间,套接字资源被移动到session::socket_
。 Server :: socket_仅拥有该资源一小部分。
移动语义允许RAII类存在于不拥有任何资源的暮光状态。在释放呼叫之后想想unique_ptr
(它指的是没有记忆)。移出后的server :: socket_仍然有空间来容纳资源,但目前它什么也没有。
lambda函数做的最后一件事是调用do_accept
,它再次调用async_accept()
。传入对socket_
的引用。当另一个客户端在将来某个时候连接时,async_accept()
将在那里转移新连接的OS套接字的所有权。
移动后,移动的对象处于与使用basic_stream_socket(io_service&)构造函数构造的状态相同的状态。
以上意味着你可以根据需要多次move
从socket
到server
的原始session
对象。