前回の続きです。
[C++]楽々TCPサーバ
前回紹介したソースなのですが、その中でtcp_connectionについて解説します。
class tcp_connection
: public boost::enable_shared_from_this<tcp_connection>{
void start_receive(){
socket_.async_receive(
boost::asio::buffer(recv_buffer_),
boost::bind(&tcp_connection::handle_receive, shared_from_this(),
boost::asio::placeholders::error, _2));
}
};
前回作成したtcp_connectionをもうちょっと見てみましょう。
よく見ると、boost::enable_shared_from_this というクラスから派生しています。
そして、async_recieveの引数をよく見ると、shared_from_thisという関数が見えます。
class udp_server{
void start_receive(){
socket_.async_receive_from(
boost::asio::buffer(recv_buffer_), remote_endpoint_,
boost::bind(&udp_server::handle_receive, this,
boost::asio::placeholders::error, _2));
}
};
同じ部分をUDPサーバで見てみましょう。
shared_from_this()の部分はただのthisですね。
UDPとTCPの差…ではありません、実体の差です。
実体…つまりどこで変数が確保されているか、です。
udp_serverクラスはmain関数内で確保されています。
では、tcp_connectionクラスはどこでしょうか。
前回にちょこっと書いたとおり、tcp_connection::create()関数が呼ばれているところを探せばいいのです。
tcp_server::start_accept()にありますね。
classs tcp_connection{
typedef boost::shared_ptr<tcp_connection> pointer;
};
class tcp_server{
void start_accept(){
tcp_connection::pointer new_connection =
tcp_connection::create(acceptor_.io_service());
acceptor_.async_accept(new_connection->socket(),
boost::bind(&tcp_server::handle_accept, this, new_connection,
boost::asio::placeholders::error));
}
};
tcp_connection::pointerってのはtcp_connectionをshared_ptrで包んだものです。
shared_ptrなので、この関数を抜けたときに開放され…ると困りますね。
handle_acceptに引数として渡されているので、handle_accept関数が終わるまでは生きています。
void handle_accept(tcp_connection::pointer new_connection,
const boost::system::error_code& error){
if (!error){
new_connection->start();
start_accept();
}
}
ここでは、もうnew_connectionを持ち続けてはいません。
この関数を抜けたところで、tcp_connectionは開放されそうです。
いや、おかしいです。
逆にいつまで生きていて欲しいか、を考えると、TCPの通信が切断されるまで生きてないと困ります。
そこで、最初のtcp_connection::start_acceptに戻ります。
thisの代わりにshared_from_thisを使っています。
つまり、自分自身のshared_ptrを待ち受けイベントに忍び込ますことで、
自分自身が開放されるのを防いでいるわけです。
そして、だれも次の待ち受けイベントを登録しなくなった次点でtcp_connectionは開放されます。
なお、shared_from_this(this)と書く代わりにboost::shared_ptr(this)ではだめなのか。
shared_ptrはすでに作られているshared_ptrを探してくるわけではありません。
つまり、ポインタを管理するクラスが2つ出来てしまい、二重管理になってしまいます。
ここで出てきたshared_from_thisを提供するクラスが、boost::enable_shared_from_thisです。
内部的に自分自身のweak_ptrを持っています。
で、必要に応じてweak_ptrをshared_ptrに変換して、渡してあげるわけです。
shared_ptrでなくweak_ptrの理由は、shared_ptrだと自分自身がもっているものを捨てないと
自分自身が解放されないという、一種の循環参照になっているためです。
ということで、shared_ptrをうまく使う事で管理するクラスを作ってないのに
必要なくなったときに解放されるというtcp_connectionクラスを作っています。
もう、なんというか無茶苦茶ですね。