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