【boost::asio buffers】 boost::asioでセッション管理にはshared_ptrが便利だ

# はじめに
C++11の素晴らしいサンプルコードを勉強したいので
boostのサンプルコードを参考書にします
他にもすばらしいコードがあれば教えて下さい！


# 今回のお題
boost1_57_0 の buffersというサンプルです
http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/example/cpp11/buffers/

いちおう、上記サンプルを CMake(CLion)プロジェクトにしたものを
下記リポジトリに置いておきます
https://github.com/MurasameOpen/CPPSample/tree/master/boostSample/boost1_57_0/asio/cpp11/buffers


# 解析

##動作
まずこのサンプルは、参照カウンタ(shared_ptr)を使い
安全にメモリ管理をするサンプルです
ポート番号を第一引数に実行し
telnetで接続してみましょう

```bash:telnet
miyatake_y$ telnet -4 localhost 8080 
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Fri Feb 13 17:40:58 2015
Connection closed by foreign host.
```

このように、現在時刻を返してコネクションをCloseするだけのものです
ただし　この後何度接続してもメモリリークをしない＆必要な時に解放されない
その管理に shared_ptr を使ってます


## ソース解析

###全体

```cpp:reference_counterd.cpp
//
// reference_counted.cpp
// ~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2014 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/asio.hpp>
#include <iostream>
#include <memory>
#include <utility>
#include <vector>

using boost::asio::ip::tcp;

// A reference-counted non-modifiable buffer class.
class shared_const_buffer
{
public:
    // Construct from a std::string.
    explicit shared_const_buffer(const std::string& data)
            : data_(new std::vector<char>(data.begin(), data.end())),
              buffer_(boost::asio::buffer(*data_))
    {
    }

    // Implement the ConstBufferSequence requirements.
    typedef boost::asio::const_buffer value_type;
    typedef const boost::asio::const_buffer* const_iterator;
    const boost::asio::const_buffer* begin() const { return &buffer_; }
    const boost::asio::const_buffer* end() const { return &buffer_ + 1; }

private:
    std::shared_ptr<std::vector<char> > data_;
    boost::asio::const_buffer buffer_;
};

class session
        : public std::enable_shared_from_this<session>
{
public:
    session(tcp::socket socket)
            : socket_(std::move(socket))
    {
    }

    void start()
    {
        do_write();
    }

private:
    void do_write()
    {
        std::time_t now = std::time(0);
        shared_const_buffer buffer(std::ctime(&now));

        auto self(shared_from_this());
        boost::asio::async_write(socket_, buffer,
                [this, self](boost::system::error_code /*ec*/, std::size_t /*length*/)
                {
                });
    }

    // The socket used to communicate with the client.
    tcp::socket socket_;
};

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();
                    }

                    do_accept();
                });
    }

    tcp::acceptor acceptor_;
    tcp::socket socket_;
};

int main(int argc, char* argv[])
{
    try
    {
        if (argc != 2)
        {
            std::cerr << "Usage: reference_counted <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;
}
```



###メインループ
```cpp:main
        boost::asio::io_service io_service;
        server s(io_service, std::atoi(argv[1]));
        io_service.run();
```
boostのお約束部分
io_serviceを作成し runで、queueにあるio命令の実行を行う
ループ処理をします



###Accept部
```cpp:server
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();
                    }

                    do_accept();
                });
    }

    tcp::acceptor acceptor_;
    tcp::socket socket_;
};
```

do_accept内の  async_acceptは、io_serviceに対して
acceptのイベント待機を行い、イベントがシグナルになると最終引数の
関数オブジェクトを実行するよう　コールバック登録し終了します
asioにおける async_xxx系の動作は全て同じになってます

そして、acceptがシグナルになった時に第三引数の関数オブジェクト(ラムダ関数)が実行されます。
ラムダ関数の最後に do_acceptを呼び出してますが、これは
acceptを受け、処理が終了した後に次のconnectを待ち受けるという
独特の書き方です
決して 再帰呼び出し（スタックが増える）していません


ラムダの中身で気になるのは２つあります

```
std::move(socket_)
```
moveセマンティクスですね。本格的に解析すると右辺値参照とか必要になるので
そういうのは外部に任せて
[本の虫](http://cpplover.blogspot.jp/2009/11/rvalue-reference_23.html)
class serverではもう socket_を使わないため、class sessionへ所有権ごと移動します



```
std::make_shared<session>
```
sessionを std::shared_ptrで管理します
shared_ptrとは参照カウンタ方式のスマートポインタで、参照個数が記録されていて
参照が無くなると自動的にdeleteされます
今回のサンプルのキモですね！

なぜshared_ptrが必要なのかは session部を見ましょう

### session部
```cpp:session
    void do_write()
    {
        std::time_t now = std::time(0);
        shared_const_buffer buffer(std::ctime(&now));

        auto self(shared_from_this());
        boost::asio::async_write(socket_, buffer,
                [this, self](boost::system::error_code /*ec*/, std::size_t /*length*/)
                {
                });
    }
```

ここも先ほどと同じように asio::async_writeで、最終引数にラムダで完了関数オブジェクトを設定してます
（今回な何もしない）
その前に

```cpp:shared_from_this()
        auto self(shared_from_this());
        boost::asio::async_write(socket_, buffer,
                [this, self](boost::system::error_code /*ec*/, std::size_t /*length*/)
                {
                });
```

この部分の処理
shared_from_this() は  std::enable_shared_from_this &lt;T&gt;を継承しているclass かつ、shared_ptrで管理されている場合にのみ有効です
class sessionは enable_shared_from_thisを継承かつ、accept時に make_sharedで管理されてるので
有効です
shared_from_this()は 自分(*this)を管理している shared_ptrを取得します
そして今回は　selfという名前で ラムダのキャプチャに入れてます

そこで注目なのは、ラムダのキャプチャにselfを指定しているが実際には使われていません！
何故使わないのにキャプチャするのかというと
参照をラムダ関数オブジェクトに残すためだと思われます

ここ、間違ってたら指摘して欲しいです。最初何をやってるか理解できませんでしたが
shared_ptrを使わず同じ実装をした時の動きを順に

### shared_ptrを使わない場合の動作
1. async_acceptを実行し、acceptハンドラを登録
2. acceptがシグナルになったのでラムダ関数が実行され 自動領域にsessionオブジェクトを作成しsession::startが呼ばれる
3. session::do_writeが呼ばれ  async_writeが実行され、writeハンドラを登録する
4. session::do_writeを抜ける、session::startを抜ける
5. acceptハンドラ を抜けると 自動領域のsessionクラスが消滅
6. writeがシグナルになった時 sessionクラスが消滅している！！


write時に sessionクラスが消滅している可能性があるわけですね
ところが　shared_ptrを使うと

2. の部分が make_sharedで shared_ptr&lt;session&gt;が作成され(参照カウンタ 1)
3. のwriteハンドラにキャプチャさせ shared_ptr&lt;session&gt;の参照カウンタincrement(参照カウンタ 2)
5. でacceptハンドラを抜けた際に shared_ptr&lt;session&gt;の参照カウンタdecrement（参照カウンタ 1）
6. writeシグナル時に 参照カウンタが残ってるのでsessionクラスを使う事ができる！
7. writeハンドラ終了時にキャプチャ解除され 参照カウンタdecrement（参照カウンタ 0） session消滅



と、正常な動作になりました！！


# 結論
asioで 非同期処理をする場合はオブジェクトの生成、解放タイミングに気をつける
shared_ptrを使うと比較的楽になる




# メモ
asio::const_buffer  については後日調べる必要がありそうだ。