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
#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 型で指定した値が負である場合は,引数なしで呼び出した場合と同じ振る舞いをします.