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

namespace ssl {
    typedef basic_sockmanager<SOCK_STREAM, AF_INET, 0> 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
  2. CLX C++ Libraries - ssl::sockmanager

References

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