2011年11月10日木曜日

BOOST::ASIOを使ってみる(3)

前回までのソースコードでboost::noncopyableの使い方が間違えていました。boost::noncopyableは簡単にクラスのコピーコンストラクタとコピー代入演算子を隠蔽するものですが、privateで継承しないと意味が無かったです。

class ServerModule : private boost::noncopyable

今回は、接続後のやり取りを分けてみたいと思います。行うことはシンプルで、非同期処理関数をクラスにまとめて分けます。

ソースコードは以下です。

ServerModule.hpp

#pragma once

#ifndef __ASIO_SERVER_MODULE__
#define __ASIO_SERVER_MODULE__

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

namespace thorny_road{

using namespace boost::asio ;

class ServerSession : private boost::noncopyable
{
private:
    io_service& io ;
    ip::tcp::socket socket ;
    boost::asio::streambuf buf ;

public:
    ServerSession(io_service& io) : io(io),socket(io) {}

    ~ServerSession() {}

    void start()
    {
        // Enqueue Read Handler
        async_read_until(
            socket,
            buf,
            '\n',
            boost::bind(&ServerSession::read_ok,this,_1) ) ;
    }

private:
    void read_ok(const boost::system::error_code e)
    {
        if (!e)
        {
            std::iostream ios(&buf) ;

            std::string tmp ;
            ios >> tmp ; // get input stream
            if (tmp == "end") { delete this; return; }
            ios << tmp <<std::endl ; // retrun as it is

            // Enqueue Write Handler
            async_write(
                socket,
                buf,
                boost::bind(&ServerSession::write_ok,this,_1) ) ;
        }
    }

    void write_ok(const boost::system::error_code e)
    {
        if (!e)
        {
            start() ; // Restart
        }
    }

public:
    ip::tcp::socket& getSocket()
    {
        return (socket) ;
    }

} ;

class ServerModule : private boost::noncopyable
{
private:
    io_service& io ;    
    ip::tcp::acceptor accept ;
    ServerSession* session ;
public:
    
    ServerModule(io_service& io, const short port)
        :io(io),accept(io,ip::tcp::endpoint(ip::tcp::v4(),port))
    {
        start_accept() ;
    }

    void start_accept()
    {
        session = new ServerSession(io) ;
        // Enqueue Accept Handler
        accept.async_accept(
            session->getSocket(),
            boost::bind(&ServerModule::accept_ok,this,_1) ) ;
    }

    void accept_ok(const boost::system::error_code e)
    {
        if (!e)
        {
            session->start() ;
            start_accept() ;
        }
        else
        {
            // error
            delete session ;
            session = NULL ;
            return ;
        }
    }

} ;

} // namespace thorny_road
#endif //__ASIO_SERVER_MODULE__


メインは変更はありません。接続後の処理はServerSessionクラスで行うようになりました。このため、SocketはServerSessionが管理するようになります。
ServerModuleでは接続完了後(accept_ok(...)が呼ばれると)、start_accept()を呼び、新たにSessionを作成し接続待ち状態になります。
ServerSessionでは前回同様入力をそのままエコーバックします。ただし、入力が"end"の場合、ServerSessionオブジェクトを破棄され、接続を切断します。

if (tmp == "end") { delete this; return; }

また、今回は簡単なboost::system::error_codeの処理をいれています。ハンドラーの引数であるboost::system::error_codeは正常の場合、0になります。

ようやく「らしく」なってきました。次はどうしようか考え中です。

ソースコードは自由にご使用ください。ただし問題が起きても責任はとれません。また、ソースコードに対する著作権は放棄していません。

0 件のコメント:

コメントを投稿