CLX C++ Libraries - sockmanager
Author: clown

CLX C++ Libraries
Home >> sockmanager

# Declarations

    template <
        int Type,
        int Family,
        int Protocol = 0
    >
    class basic_sockmanager;

    namespace udp {
        typedef basic_sockmanager<SOCK_DGRAM, AF_INET, 0> sockmanager;
    };

    namespace tcp {
        typedef basic_sockmanager<SOCK_STREAM, AF_INET, 0> sockmanager;
    };

    namespace icmp {
        typedef basic_sockmanager<SOCK_RAW, AF_INET, IPPROTO_ICMP> sockmanager;
    };

# Overview

sockmanager は，複数のソケットへの入力状況を管理するためのクラスです．fd_set および select() システムコールを用いて実装しています．sockmanager は， 何らかのデータがソケットへ到着すると，あらかじめそのソケットに対して登録しておいた handler を呼び出します．登録する handler は関数ポインタ，または関数を利用できます． 例えば，TCP 通信用の handler を作成する場合は以下のような形になります．

    // 関数の場合
    bool func(clx::tcp::rawsocket* s, clx::tcp::sockmanager& sm);

    // 関数オブジェクトの場合
    class handler {
    public:
        template <class SockT, class SockManager>
        bool operator()(SockT* s, SockManager& sm);
    };

指定した関数，または関数オブジェクトの () 演算子が false を返した場合， sockmanager はそのソケットオブジェクトを監視リストから外します． sockhandler では，tcp::accept_handler<...> など，よく使われそうな handler をいくつか実装しています．

sockmanager は，ソケットの情報を shared_ptr を利用して管理しています． new 演算子で確保した領域は shared_ptr より自動的に開放されるため，自分で delete 演算子を用いて開放しないで下さい．

# Examples

example_sockmanager_recv.cpp

    #include <iostream>
    #include <cstring>
    #include "clx/tcp.h"
    #include "clx/sockhandler.h"

    /* ------------------------------------------------------------------------- */
    //  chat_service
    /* ------------------------------------------------------------------------- */
    class chat_service {
    public:
        typedef clx::tcp::sockstream socket_type;

        chat_service(socket_int s) : accid_(s) {}

        template <class T, class SockManager>
        bool operator()(T* s, SockManager& sm) {
            socket_type* ss = dynamic_cast<socket_type*>(s);

            std::string ipaddr = ss->address().ipaddr();
            int port = ss->address().port();
            std::string buf;
            if (!std::getline(*ss, buf)) {
                std::cout << ipaddr << ':' << port
                    << ": connection was closed" << std::endl;
                ss->close();
                return false;
            }

            /*
             * 接続されている全クライアントに受信したメッセージを
             * message (xxx.xxx.xxx.xxx:port)
             * という形で送信する．ただし，sockmanager には接続確立用の
             * ソケットも登録されてあるため，そのソケットには送らないように
             * 注意する．コンストラクタで接続確立用のソケットのソケット番号
             * を知らせてもらっている (accid_) ので，その値を基に判断する．
             */
            buf += " (" + ipaddr + ":" + clx::lexical_cast<std::string>(port) + ")";
            for (typename SockManager::iterator pos = sm.begin(); pos != sm.end(); pos++) {
                if (sm.socket(pos)->socket() == accid_) continue;
                socket_type* os = dynamic_cast<socket_type*>(sm.socket(pos));
                *os << buf << std::endl;
            }

            return true;
        }

    private:
        socket_int accid_; // acceptorのソケット番号
    };

    /* ------------------------------------------------------------------------- */
    //  main
    /* ------------------------------------------------------------------------- */
    int main(int argc, char* argv[]) {
        typedef clx::tcp::accept_handler<clx::tcp::sockstream,
            chat_service> handler_type;

        try {
            clx::tcp::sockmanager s;
            int port = clx::tcp::port(argv[1]);
            s.add(new clx::tcp::acceptor(port), handler_type());
            s.start();
        }
        catch (clx::socket_error& e) {
            std::cerr << e.what() << std::endl;
            return -1;
        }
        catch (clx::sockaddress_error& e) {
            std::cerr << e.what() << std::endl;
            return -1;
        }

        return 0;
    }

    Result
    server: (192.168.0.2)
    192.168.0.3:2419: connection was closed
    192.168.0.2:2335: connection was closed

    client1: (192.168.0.2)
    Hello, world! (192.168.0.3:2419)
    This is a test program.
    This is a test program. (192.168.0.2:2335)
    bye bye.
    bye bye. (192.168.0.2:2335)
    bye. (192.168.0.3:2419)

    client2: (192.168.0.3)
    Hello, world!
    Hello, world! (192.168.0.3:2419)
    This is a test program. (192.168.0.2:2335)
    bye bye. (192.168.0.2:2335)
    bye.
    bye. (192.168.0.3:2419)

クライアント用のプログラムの掲載は省略します．クライアント用のサンプルコードは example_sockmanager_send.cpp から取得して下さい．サーバ側の接続要求受け付け処理を行う handler は，sockhandler で定義している accept_handler を利用しています． 各ソケットと同時に標準入力も監視する場合，new basic_rawsocket<Type, Family>(0) で監視用オブジェクトを生成した後，sockmanager へ （標準入力へデータが到着したときに行う処理を定義した handler とともに）登録します （example_sockmanager_send.cpp を参照）．ただし，この方法は UNIX 系でしか使えないようです．

# Template Parameters

Type
  ソケットタイプを指定します．
Family
  プロトコルファミリーを指定します．

# Related Types

    typedef basic_rawsocket<Type, Family> rawsocket_type;
    typedef detail::sockhandler_base<Type, Family> basehandler_type;
    typedef std::map<shared_ptr<rawsocket_type>, shared_ptr<basehandler_type>,
        detail::socket_less<rawsocket_type> > rfdset;
    typedef typename rfdset::size_type size_type;
    typedef typename rfdset::iterator iterator;
    typedef typename rfdset::const_iterator const_iterator;
    typedef struct timeval timeval_type;

# Construction and Member Functions

    basic_sockmanager();
    virtual ~basic_sockmanager();

    void start();
    void start(const timeval_type& t);
    void start(double sec);
    void stop();
    void reset();

    template <Functor>
    bool add(rasocket_type* s, Functor h);
    bool remove(rawsocket_type* s);

    size_type size() const;
    bool empty() const;

    iterator begin();
    iterator end();
    rawsocket_type* socket(iterator pos);

    const_iterator begin() const;
    const_iterator end() const;
    const rawsocket_type* socket(const_iterator pos) const;

start() メソッドは引数なしの場合，sockmanager に登録されているソケット数がゼロになるか， stop() メソッドが呼ばれるまで監視（ブロック）し続けます．引数に時間を指定した場合は， 指定した時間が経過すると監視を終了します．時間は，timeval 構造体， もしくは秒単位の double 型で指定します．引数として 1 時間を指定する場合は， 対応する秒数を直接指定する他に hours(1) と記述することもできます． 同様の補助関数として，hours()，minutes()，seconds()，microsec() を定義しています． 尚，double 型で指定した値が負である場合は，引数なしで呼び出した場合と同じ振る舞いをします．

# Related Pages

1. CLX C++ Libraries - sockhandler

# References

1. Life like a clown - ICMPパケットを利用したパケット間隔ベースの帯域計測方法
2. Life like a clown - ネットワーク帯域計測プログラム
3. Boost C++ Libraries - Smart Pointers