Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > удаленный вызов. детали, реализация, архитектура


Автор: boostcoder 6.10.2010, 11:23
всем доброго дня.
имеем тип T:
Код

template<typename T>
struct any_type {
   enum { id = unique_id };
   typedef ... get_type;
   typedef ... set_type;
};

в runtime мы получаем id`ы.
хочу реализовать что-то вроде типозависимого прокси callable`ра(хз как правильно назвать).

сигнатура методов/объектов такая:
Код

void method(T::set_type& set, const T::get_type& get);


нужна возможность регистрировать методы/объекты имея в runtime только id, и ин compile_time T::id.
т.е. зная T::id ат compile_time и id в runtime, назначать методы/объекты принимающие соответствующие им типы.

пример конечного использования:
Код

// any types
struct type1 { id = ... };
struct type2 { id = ... };
struct type3 { id = ... };
...
struct proxy { // registrator
   template<typename T>
   void reg(method_with_needed_signature_ptr);
};
// methods impl
...
void method1(type1::set_type& set, const type1::get_type& get) {}

void method2(type2::set_type& set, const type2::get_type& get) {}

void method3(type3::set_type& set, const type3::get_type& get) {}
...
// reg in proxy
proxy.reg<type1>(method1);
proxy.reg<type2>(method2);
proxy.reg<type3>(method3);

...
// call proxy
int id = ...
type1 arg = ...
proxy(id)(arg.set_type, arg.get_type);


как-то так...
запутался, и ничего в голову не лезет...

Автор: xvr 6.10.2010, 11:32
Цитата(boostcoder @  6.10.2010,  11:23 Найти цитируемый пост)
нужна возможность регистрировать методы/объекты имея в runtime только id, и ин compile_time T::id.

IMHO сие невозможно. Либо id должен быть известен в compile time, либо реализация proxy вообще не должна зависеть от id

Либо я не понял требования задачи - что с чем и по каким признакам надо связать? Во что должен развернуться
Код

proxy(id)(arg.set_type, arg.get_type);
?


Автор: mes 6.10.2010, 11:46
Цитата(boostcoder @  6.10.2010,  10:23 Найти цитируемый пост)
зная T::id ат compile_time и id в runtime

это разные числа , или одно и то же ? 

Автор: boostcoder 6.10.2010, 17:19
Цитата(xvr @  6.10.2010,  11:32 Найти цитируемый пост)
Во что должен развернуться

если id == type1::id, то должен произойти вызов method1(...)


Цитата(mes @  6.10.2010,  11:46 Найти цитируемый пост)
это разные числа , или одно и то же ?

int id имеет диапазон type1::id ... typeN::id

два уточнения:
1. typeN::id - это enum. так что можно использовать в mpl.
2. у всех typeN общий предок.

Автор: mes 7.10.2010, 01:02
было б лучше если б дали немного сведений и о задачи, а именно какие требования предъявляются к рантайм..

вот на полусне : http://liveworkspace.org/code/c8cf16a805aa19cb10bc837af0545038
в том хоть направлении ?

Добавлено через 3 минуты и 27 секунд
это Вы удаленный вызов реализуете ?

Добавлено через 5 минут и 19 секунд
Цитата(boostcoder @  6.10.2010,  16:19 Найти цитируемый пост)
2. у всех typeN общий предок.

Цитата(boostcoder @  6.10.2010,  10:23 Найти цитируемый пост)
(type1::set_type& set, const type1::get_type& get) 

а что ж тогда не объект класса typeN передается  ?



Автор: maxim1000 7.10.2010, 08:44
Цитата(boostcoder @  6.10.2010,  11:23 Найти цитируемый пост)
proxy(id)(arg.set_type, arg.get_type);

в параметрах должны быть типы или значения соответствующих типов?

Автор: boostcoder 7.10.2010, 08:50
Цитата(mes @  7.10.2010,  01:02 Найти цитируемый пост)
это Вы удаленный вызов реализуете ?

да.

Цитата(mes @  7.10.2010,  01:02 Найти цитируемый пост)
в том хоть направлении ?

похоже на то. покурю...

Цитата(mes @  7.10.2010,  01:02 Найти цитируемый пост)
а что ж тогда не объект класса typeN передается  ?

можно и тип. но задача не упростится. я для пользователя удобней и понятней.

Цитата(maxim1000 @  7.10.2010,  08:44 Найти цитируемый пост)
в параметрах должны быть типы или значения соответствующих типов?

значения.

Автор: mes 7.10.2010, 09:24
Цитата(boostcoder @  7.10.2010,  07:50 Найти цитируемый пост)
можно и тип. но задача не упростится. я для пользователя удобней и понятней.

при использовании структуры кол-во аргументов функции постоянно.

Автор: mes 7.10.2010, 15:48
так же удобство структуры в возможности легкой (мета ориентированной) регистрации ,
т.е. для неперегруженных функций достаточно только передать имя функции :
Код

template <class T> 
void register_handler ( void (*fn)(const T&) )
{
     arr[T::id] = (fn_t)fn;    
}
 

register_handler (&f1);



подправленная версия :  http://liveworkspace.org/code/3b42cd08948eddfa684cf01e63c3374e

Автор: maxim1000 8.10.2010, 00:10
можно упаковывать полученные аргументы в boost::any
а методы оборачивать во что-то шаблонное, унаследованное от общего интерфейса (который и принимает эти boost::any)

Добавлено через 55 секунд
(правда, не получится пользоваться неявными преобразованиями)

Автор: mes 8.10.2010, 09:40
в примере обработчиками выступают функции, но у конечному пользователь захочется иметь что либо более универсальное..поможет класс Dispatchera, для конвертации параметра и класс функции (напр boost::function)
http://liveworkspace.org/code/09b6daf1e59dd1131aee0c15a1aff76d

на "ляпы"  не относящиеся к сути проблемы (такие как неподходящий способ хранения) внимания не обращать smile

Автор: mes 8.10.2010, 22:23
кстати вспомнил, goldfinch  одно время на форуме разбирал темы связанные с удаленными вызовами..
не помню к чему он там конкретно пришел, но думаю Вам будет тематически полезно ознакомиться с этим материалом.. 
если найдете прикрепите ссылки к теме, плиз
smile

Автор: boostcoder 9.10.2010, 03:12
Цитата(mes @  8.10.2010,  22:23 Найти цитируемый пост)
кстати вспомнил, goldfinch

я даже аккаунта такого не могу найти. и функцию поиска аккаунтов тоже :(

пока курю ваш пример. хочется автоматизировать это все.
написать кодогенератор на препроцессоре - не проблема. но есть несколько неудобных моментов. думаю...

Автор: boostcoder 9.10.2010, 07:15
mes, сделал так, как вы предложили:
Код

/***************************************************************************/

struct i_dispatcher {
   virtual ~i_dispatcher() {}
   virtual void dispatch(constants::header_packet&, constants::body_packet&) = 0;
};

/***************************************************************************/

template<typename C, typename T>
struct methods_dispatcher: i_dispatcher {
   typedef void(C::*handler_t)(typename T::result_type&, const typename T::query_type&);
   /**  */
   methods_dispatcher(C& p, handler_t h):_this(p),_handler(h) {}

   virtual void dispatch(constants::header_packet& h, constants::body_packet& b) {
      typename T::query_type query = deserializer(h, b);
      typename T::result_type result;
      (_this.*_handler)(result, query);
      serializer(h, b, result);
   }

private:
   C& _this;
   handler_t _handler;
};

/***************************************************************************/

template<typename C>
struct proxy_caller {
   proxy_caller(C* p, constants::header_packet& h, constants::body_packet& b)
      :_parent(p),_header(h),_body(b)
   {}

   template<typename T>
   void register_handler(typename methods_dispatcher<C, T>::handler_t handler) {
      if ( _map.find(T::id) != _map.end() ) {
         throw std::runtime_error("id(" +  boost::lexical_cast<std::string>((int)T::id) + ") already registered");
      }
      _map[T::id] = dispatcher_ptr(new methods_dispatcher<C, T>(*_parent, handler));
   }

   void call() {
      constants::header hdr = constants::decode_header(_header);
      _map[hdr.id]->dispatch(_header, _body);
   }

private:
   C* _parent;
   constants::header_packet& _header;
   constants::body_packet& _body;
   typedef boost::shared_ptr<i_dispatcher> dispatcher_ptr;
   std::map<int, dispatcher_ptr> _map;
};

/***************************************************************************/


используется это дело так:
Код

struct implementation {
   implementation():_proxy(this, ....) {
      _proxy.register_handler<type1>(&implementation::method1);
      _proxy.register_handler<type2>(&implementation::method2);
   }

   void method1(type1::result_type& r, const type1::query_type& q) {
   }
   void method2(type2::result_type& r, const type2::query_type& q) {
   }

   proxy_caller<implementation> _proxy;
};


метод proxy_caller::call() вызывается из сетевой части после того, как буфера на которые ссылается proxy_caller заполнены.
как видно из реализации метода methods_dispatcher::dispatch(), ответ отправляется обратно при выходе из тела этого метода.
не худшая реализация RPC из тех что я видел smile 
но хочется большего.
а именно: скрыть от юзера все, кроме самого implementation.

в идеале, цель хочу видеть такой:
Код

// декларация интерфейса некоторого типа.
INTERFACE_BEGIN(transaction_type)
   METHOD(
      std::string, // ret
      login, // method name
      std::string, // method arg
      std::string // method arg
   )
   METHOD(
      std::string, // ret
      get_balance, // method name
      std::string, // method arg
      std::string // method arg
   )
INTERFACE_END()

// код клиента
client<transaction_type> client(host, port);
std::string ret = client.login("nick", "pass");
ret = client.get_balance("date from", "date to");

но тут есть непонятка...
т.к. клиент, всего лишь дергает методы сервера, ему их реализация не нужна. но она нужна серверу.
вот и непонятка: что на самом деле нужно на стороне клиента, чтоб дергать методы сервера? и что нужно знать серверу, чтоб понять, какой именно метод вызывает клиент?

Автор: mes 9.10.2010, 10:44
Цитата(boostcoder @  9.10.2010,  02:12 Найти цитируемый пост)
я даже аккаунта такого не могу найти. 

ну проще было искать тему, и поставить ограничение по юзеру..
вобщем вот об этой я говорил :
http://forum.vingrad.ru/forum/topic-269086/unread-1/hl/remote/index.html#st_0_view_0

Автор: boostcoder 9.10.2010, 10:52
почитал.
но в теме не нашел ничего полезного касательно реализации. а готовое не устраивает по ряду причин, обсуждать которые нет желания.

Автор: mes 9.10.2010, 10:54
Цитата(boostcoder @  9.10.2010,  09:52 Найти цитируемый пост)
но в теме не нашел ничего полезного касательно реализации.

да, я тоже прочитал, по старой памяти думал там больше полезного smile

Добавлено @ 11:03
вот например метод login... напишите отдельно, как Вы представляете, клиентскую и серверную часть функционала не затрагивая реализацию rpc, т.е. фактически конечные точки Вашей схемы.

Автор: boostcoder 9.10.2010, 11:23
mes, а что именно описать? как это все происходит сейчас? или что?...
метод login - просто метод. вызывая его на стороне клиента, его аргументы отправляются на сервер, десериализуются, вызывается назначенный для этого типа запроса обработчик, тот в свою очередь получает дополнительный аргумент по ссылке в который кладет ответ, ответ сериализуется, отправляется клиенту, клиент десериализует его, и возвращает юзеру объект типа ответа.

Автор: mes 9.10.2010, 11:34
Цитата(boostcoder @  9.10.2010,  10:23 Найти цитируемый пост)
 а что именно описать? как это все происходит сейчас? или что?...

имелся ввиду код взаимодействия.. как Вы его видите..
например так :
Код

// client 
void login  () {
   login_t::result res << connection << login_t::query("name", "pass");

}
// server
void on_login (login_t::query cont & q )
{
     if (q.name==  ... && q.pass ==..  )
      connection << login_t::result (eOK);
  else ...
}


Добавлено @ 11:37
Цитата(boostcoder @  9.10.2010,  10:23 Найти цитируемый пост)
, его аргументы отправляются на сервер, десериализуются, вызывается назначенный для этого типа запроса обработчик, тот в свою очередь получает дополнительный аргумент по ссылке в который кладет ответ, ответ сериализуется, отправляется клиенту, клиент десериализует его, и возвращает юзеру объект типа ответа.

это как раз лишнее, так как  
Цитата(mes @  9.10.2010,  09:54 Найти цитируемый пост)
 не затрагивая реализацию rpc,


Автор: boostcoder 9.10.2010, 11:44
понял.
вот нынешний реальный код на стороне клиента. он даже работает smile 
Код

      client client(argv[1], std::atoi(argv[2]));
      /**  */
      connectToServer::value_type ret = client.query<connectToServer>(std::string("John"));
      std::cout << ret->msg() << std::endl;
      getUserList::value_type ret1 = client.query<getUserList>(11);
      std::cout << ret1->users()[0] << std::endl;


client::query<>() возвращает смарт-поинтер.

Добавлено через 2 минуты и 25 секунд
Цитата(mes @  9.10.2010,  11:34 Найти цитируемый пост)
// client 
void login  () {
   login_t::result res << connection << login_t::query("name", "pass");
}
// server
void on_login (login_t::query cont & q )
{
     if (q.name==  ... && q.pass ==..  )
      connection << login_t::result (eOK);
  else ...
}

что-то у меня это по удобнее получилось smile

Добавлено через 4 минуты и 21 секунду
на стороне сервера, каждый тип команды, ассоциирован с методом обработчиком.

Автор: mes 9.10.2010, 11:49
первое что бросается в глаза, value_type не очень удачное название.. 

Автор: boostcoder 9.10.2010, 11:51
mes, почему? как тогда его лучше назвать?
мне многие названия в моем коде не нравятся, наверное фантазии не хватает)

Автор: mes 9.10.2010, 11:51
второе, что у Вас не ассинхронный обмен - так и нужно ?

Автор: boostcoder 9.10.2010, 11:53
Цитата(mes @  9.10.2010,  11:51 Найти цитируемый пост)
не ассинхронный обмен

асинхронный до тех пор, пока не вызывается один из методов value_type. иначе, вызывающий поток приостанавливается на время получения ответа от сервера. только еще нужно таймаут прикрутить.

Автор: mes 9.10.2010, 11:55
Цитата(boostcoder @  9.10.2010,  10:51 Найти цитируемый пост)
как тогда его лучше назвать?

как не знаю.. просто  за value_type уже закрепилось некоторое смысловое значение, которое не совпадает с приведенным применением.. 
ну например response_type или return_type ..

Добавлено через 2 минуты и 36 секунд
Цитата(boostcoder @  9.10.2010,  10:53 Найти цитируемый пост)
пока не вызывается один из методов value_type.

не до конца понял..
запрос к серверу идет здесь
Цитата(boostcoder @  9.10.2010,  10:44 Найти цитируемый пост)
  connectToServer::value_type ret = client.query<connectToServer>(std::string("John"));

или здесь 
Цитата(boostcoder @  9.10.2010,  10:44 Найти цитируемый пост)
    std::cout << ret->msg() << std::endl;

?

Автор: boostcoder 9.10.2010, 11:58
Цитата(mes @  9.10.2010,  11:55 Найти цитируемый пост)
или return_type

да, так лучше.

Автор: mes 9.10.2010, 11:59
Цитата(boostcoder @  9.10.2010,  10:53 Найти цитируемый пост)
 иначе, вызывающий поток приостанавливается

т.е. ассинхронность введена специально ? или все ж так получилось ?

Добавлено через 4 минуты и 43 секунды
Цитата(boostcoder @  9.10.2010,  10:44 Найти цитируемый пост)
что-то у меня это по удобнее получилось 

ну так не удивительно.. я то написал спонтанно smile

Автор: boostcoder 9.10.2010, 12:07
здесь:
Цитата

connectToServer::value_type ret = client.query<connectToServer>(std::string("John"));

он ставится в очередь на основе boost::thread + asio::io_service + boost::packaged_task<>

но при вызове любого из методов результата, вызывается boost::unique_future::get(), который приостанавливает вызывающий цикл.

Добавлено @ 12:08
Цитата(mes @  9.10.2010,  11:59 Найти цитируемый пост)
т.е. ассинхронность введена специально ?

да, специально.

Добавлено @ 12:14
хотя в этом есть некоторое не очевидное поведение: если вдруг asio::io_service::run выполнить в нескольких потоках(типа пул потоков), и сделать один запрос к серверу ничего не возвращающий но сохраняющий в объект сессии некоторое значение, и следом за ним сделать запрос получающий это значение, то может получится так, что второй запрос придет на сервер раньше smile 
чтоб исключить такую ситуацию, в тип нужно добавить служебный метод, что-то типа wait(). сделаю... позже..

Добавлено @ 12:19
Код

client client(...);
client.query<put_t>(33); // т.к. нам не интересно, уже доставилось значение, или нет
get_t::return_type res1 = client.query<get_t>(); // тут мы можем попытаться получить его в то время как оно еще в_очереди/в_пути

Автор: boostcoder 9.10.2010, 12:41
собственно сейчас меня интересует, каким образом можно авторегистрировать методы-обработчики на стороне сервера, учитывая такую структуру:
Код

#include <iostream>
#include <boost/shared_ptr.hpp>

struct implementation {
   void method() {}
};

template<typename C>
struct proxy {
   proxy(C* p):_parent(*p) {}

   template<typename T>
   void register_handler(void (C::*handler)()) {
   }
   
   C& _parent;
};

template<typename IF>
struct connection {
   connection():_impl(new IF),_proxy(_impl.get()) {
      _proxy.template register_handler<int>(&IF::method);
   }
   boost::shared_ptr<IF> _impl;
   proxy<IF> _proxy;
};

int main() {
   connection<implementation> context;
}


http://liveworkspace.org/?id=bb4bddf94368b5924b96872fa347ec98
connection - объект сессии с клиентом.

Автор: mes 9.10.2010, 12:41
интуитивно напрашивается такой псевдо-код :
Код

   rpc<login_t> 
     login(client, login_t::query("John"));

   login.wait(100);

   std::cout << login.result;

где rpc это шаблонная структура обертка, отвечающая за отправку запроса клиенту, ожидание и предоставления доступа к результату.

Добавлено через 3 минуты и 6 секунд
Цитата(boostcoder @  9.10.2010,  11:41 Найти цитируемый пост)
, каким образом можно авторегистрировать методы-обработчики на стороне сервера, 

не приглядывался но мне кажется вы хотите, чтоб у сервера на все пакеты были перегруженные функции с одним именем..
тогда вам поможет список_типов..  

Автор: boostcoder 9.10.2010, 12:46
для этого, нужен метагенератор, генерирующий метаинформацию из к примеру, такого макроса:
Код

INTERFACE_BEGIN(transaction_type)
   METHOD(
      std::string, // ret
      login, // method name
      std::string, // method arg
      std::string // method arg
   )
   METHOD(
      std::string, // ret
      get_balance, // method name
      std::string, // method arg
      std::string // method arg
   )
INTERFACE_END()

если принять за правило соответствие деклараций методов в этом макросе и в реализации этого класса на стороне сервера, то задача кажется вполне выполнимой.

т.е. для этого интерфейса должна быть такая реализация:
Код

struct transaction_type {
   std::string login(std::string, std::string) {}
   std::string get_balance(std::string, std::string) {}
};


есть идеи?

Добавлено через 4 минуты и 44 секунды
Цитата(mes @  9.10.2010,  12:41 Найти цитируемый пост)
интуитивно напрашивается такой псевдо-код

вариант.
еще вариант, сделать так, чтоб ожидание происходило в деструкторе типа ответа. т.к. он в смарт-поинтере, если мы не приаттачились к нему, то в деструкторе ждать.

Цитата(mes @  9.10.2010,  12:41 Найти цитируемый пост)
вы хотите, чтоб у сервера на все пакеты были перегруженные функции с одним именем..

нет. на каждый тип команды свой метод обработчик на сервере.

Автор: mes 9.10.2010, 12:52
Цитата(boostcoder @  9.10.2010,  11:46 Найти цитируемый пост)
есть идеи? 

в данном случае не понял идеи чего нужны... как из макроса генерить описание серверного обработчика ?

сообщение про список типов видели ? если приведете функции обработчики к одному имени, то выгадаете (не кодогенеруеммую, а шаблонную) автоматизацию
smile
Цитата(boostcoder @  9.10.2010,  11:46 Найти цитируемый пост)
 на каждый тип команды свой метод обработчик на сервере.

естественно разный, но с таким же именем ))


Автор: boostcoder 9.10.2010, 12:54
Цитата(mes @  9.10.2010,  12:52 Найти цитируемый пост)
как из макроса генерить описание серверного обработчика ? 

 smile нет.

я не очень представляю, что именно макрос должен сгенерировать, чтоб без реализации на стороне клиента слинковать программу, а на стороне сервера понять что к чему и как вязать.

Добавлено через 2 минуты и 48 секунд
Цитата(mes @  9.10.2010,  12:41 Найти цитируемый пост)
тогда вам поможет список_типов..

что имеется в виду?

Добавлено через 5 минут и 26 секунд
Цитата(mes @  9.10.2010,  12:52 Найти цитируемый пост)
естественно разный, но с таким же именем

имена у них тоже разные:
Код

struct implementation {
   implementation() {}

   void connectToServer_handler(connectToServer::result_type& r, const connectToServer::query_type& q);
   void getUserList_handler(getUserList::result_type& r, const getUserList::query_type& q);
};


Автор: mes 9.10.2010, 13:13
Цитата(boostcoder @  9.10.2010,  11:54 Найти цитируемый пост)
что имеется в виду?

у буста что то в этом роде : 

Код

boost::mpl::for_each (register, boost::mpl::list<type1, type2, type3>);


ну а сервер выглядит так
Код

struct server {
  void handle (type1 &);
  void handle (type2 &);
  void handle (type3 &);

};

Автор: boostcoder 9.10.2010, 13:19
т.е. вы предлагаете решение, в котором для идентификации методов используется список типов аргументов? я правильно Вас понял?

Цитата(mes @  9.10.2010,  13:13 Найти цитируемый пост)
struct server {
  void handle (type1 &);
  void handle (type2 &);
  void handle (type3 &);
};

неудобно тем, что имена одинаковые.
разобрать аргументы метода на список типов, и собрать их обратно - не проблема. проблема именно в имени метода.

Автор: mes 9.10.2010, 13:25
Цитата(boostcoder @  9.10.2010,  12:19 Найти цитируемый пост)
неудобно тем, что имена одинаковые.
разобрать аргументы метода на список типов, и собрать их обратно - не проблема. проблема именно в имени метода.

фактически именем метода будет выступать имя типа пакета..
а чем неудобно одинаковое имя ?

Добавлено через 51 секунду
Цитата(boostcoder @  9.10.2010,  12:19 Найти цитируемый пост)
 в котором для идентификации методов используется список типов аргументов?

да список типов будет определять какие обработчики предоставляет сервер.

Добавлено через 1 минуту и 58 секунд
Цитата(boostcoder @  9.10.2010,  12:19 Найти цитируемый пост)
разобрать аргументы метода на список типов, и собрать их обратно - не проблема. 

а вот разбирать ничего не надо.. типы должны задаваться интерфейсом, который вы кодогенерируете..

Добавлено через 2 минуты и 50 секунд
не обязательно напрямую, возможно через прослойку, только внешне не видную.

Добавлено через 4 минуты и 56 секунд
сейчас еще одна идея пришла... надо обдумать.. 

Автор: boostcoder 9.10.2010, 14:11
Цитата(mes @  9.10.2010,  13:25 Найти цитируемый пост)
типы должны задаваться интерфейсом, который вы кодогенерируете..

имел в виду их и разобрать.

пока не представляю, что должно генерироваться на стороне клиента, чтоб прога скомпилилась..
на стороне сервера - понятно, из сгенерированного кода берется информация необходимая для связывания указателя на объект реализации интерфейса с которым должна связываться.

Автор: mes 9.10.2010, 14:33
Цитата(boostcoder @  9.10.2010,  13:11 Найти цитируемый пост)
пока не представляю, что должно генерироваться на стороне клиента, чтоб прога скомпилилась..

клиент заканчивается отправкой сериализированного объекта в _сокет_.

Добавлено через 1 минуту и 30 секунд
хотя да.. я забыл что еще ответ надо ждать.. вот тут есть проблемка... 

Автор: boostcoder 9.10.2010, 14:45
Цитата(mes @  9.10.2010,  14:33 Найти цитируемый пост)
хотя да.. я забыл что еще ответ надо ждать.. вот тут есть проблемка...

угу.
но вроде как можно приблизительно так(мысли в слух):
на стороне клиента, сгенериный интерфейс используется совместно с классом client<interface>
на стороне сервера, сгенериный интерфейс используется совместно с классом server<interface>
то что генерится, должно содержать в себе какое-то значение времени компиляции которое client<> и server<> будут читать, то можно реализовать их таким образом, чтоб каждый брал для себя только то, что ему нужно. 

клиенту, на самом деле, нужны эти методы реализованными, но тело этих методов должно посылать запрос на сервер, содержащий в себе помимо самого запроса, еще и информацию идентифицирующую запрос. тогда сервер сможет понять чего от него хотят.

серверу, нужна полная реализация, методы которой, вызываются из оберток.

как-то так...

Автор: boostcoder 9.10.2010, 15:07
Цитата(boostcoder @  9.10.2010,  14:45 Найти цитируемый пост)
можно реализовать их таким образом, чтоб каждый брал для себя только то, что ему нужно.


даже не так...
получается так, что оба используют сгенериный интерфейс только для того, чтоб иметь дополнительную информацию о вызове.
к примеру:
Код

// описание интерфейса из которого будет сгенерирован метакод.
INTERFACE_BEGIN(type1)
   METHOD(int, add, int, int);
   METHOD(int, sub, int, int);
INTERFACE_END()

// вызов процедуры так:
client<type1> client(...);
int result = client.add(2,1); // это было бы идеальным вариантом

// на стороне сервера, реализация должна выглядеть так:
struct type1 {
   int add(int a, int b) {
      return a+b;
   }
   int sub(int a, int b) {
      return a-b;
   }
};
// но, т.к. реализацию пишем мы сами, сервер в первую очередь должен уметь связывать 
// информацию полученную о вызываемом методе от клиента.
server<type1> server(...); // сервер только знает о том, с каким типом ему предстоит работать.
// при входящем подключении, сервер, создает динамический объект type1, связывает запросы от клиента с реальным объектом.


и возвращаемся к старому вопросу:
1. что должен генерировать метагенератор на стороне клиента?
2. что должен генерировать метагенератор на стороне сервера?
3. как связать сгенерированную информацию с реальным, написанным нами, классом?

Автор: mes 9.10.2010, 15:20
тоже мысли вслух.. 
у нас есть запрос мы отправляем его серверу, от него получаем ответ.. 
тут нужна гарантия определения к какому запросу этот ответ
1. либо у нас строгая очередность, и нет следующего запроса пока не получет ответ. имхо, не имеет смысла 

2. запрос сохраняется и выделяется уникальный нумер операции 
  т.е. тогда возможны два запроса подряд одного типа. 

3. каждый вид запроса может существовать только в единственном экземпляре.. 
 но тут опять видна несогласованность, ответ может придти на  предыдущий запрос, который мы отменили..

Добавлено через 4 минуты и 45 секунд
насчет сервера... у нас есть некий компонент, который локально выполняет некую работу..
мы должны определить связку между запросом и компонентом..
т.е. на каждый тип запроса у нас должен быть бинд забирающий инфу откомпонента.. 
по идеи значит надо для диспетчера создать таблицу лямб функций возвращаюших ответ, на запрос..

Добавлено через 5 минут и 18 секунд
ну так и получается, что имя функции нам не нужно.. 

Автор: boostcoder 9.10.2010, 15:26
Цитата(mes @  9.10.2010,  15:20 Найти цитируемый пост)
1. либо у нас строгая очередность, и нет следующего запроса пока не получет ответ. имхо, не имеет смысла

так и запланировано.
выше, я просто привел пример, каким образом можно получить неочередность. а так как запросы живут в одном io_service, то все они выполняются строго в том порядке, в каком были добавлены в очередь.

Автор: mes 9.10.2010, 15:30
Цитата(boostcoder @  9.10.2010,  14:26 Найти цитируемый пост)
а так как запросы живут в одном io_service, то все они выполняются строго в том порядке, в каком были добавлены в очередь. 

т.е. в сеть не отправляется ничего, до тех пора пока не получен ответ ? имхо не рационально .. 

Автор: mes 9.10.2010, 17:38
кстати при подобной архитектуре сервер получается пассивным, т.е. сообщает только ту информацию которую запросил клиент..
однако судя по названиям некоторых типов Вы разрабатываете нечто вроде чата/игры.. В подобных случаях очень часто бывает необходимость, о сообщение клиенту деталей без его запроса.. 

Автор: boostcoder 9.10.2010, 20:25
Цитата(mes @ 9.10.2010,  15:30)
Цитата(boostcoder @  9.10.2010,  14:26 Найти цитируемый пост)
а так как запросы живут в одном io_service, то все они выполняются строго в том порядке, в каком были добавлены в очередь. 

т.е. в сеть не отправляется ничего, до тех пора пока не получен ответ ? имхо не рационально ..

да.
я понимаю, что можно запросам присваивать ID`ы. но это пока не главный вопрос.

Добавлено через 3 минуты и 18 секунд
Цитата(mes @  9.10.2010,  17:38 Найти цитируемый пост)
кстати при подобной архитектуре сервер получается пассивным, т.е. сообщает только ту информацию которую запросил клиент..

да.

Цитата(mes @  9.10.2010,  17:38 Найти цитируемый пост)
очень часто бывает необходимость, о сообщение клиенту деталей без его запроса..

можно реализовать следующим образом: в client<> добавить deadline_timer, который сам будет слать серверу запросы, и при получении ответов, вызывать некоторый обработчик.

но и это не главное.

Цитата(boostcoder @  9.10.2010,  15:07 Найти цитируемый пост)
1. что должен генерировать метагенератор на стороне клиента?
2. что должен генерировать метагенератор на стороне сервера?
3. как связать сгенерированную информацию с реальным, написанным нами, классом? 


Автор: mes 9.10.2010, 20:58
Цитата(boostcoder @  9.10.2010,  19:25 Найти цитируемый пост)
1. что должен генерировать метагенератор на стороне клиента?
2. что должен генерировать метагенератор на стороне сервера?
3. как связать сгенерированную информацию с реальным, написанным нами, классом? 


имхо вопрос не с той стороны задан.. 
для начала нужно представить модель, а как потом это все запихнуть в кодогенерацию дело техники 
smile

Добавлено через 39 секунд
т.е. для начала надо представить как будет выглядить код..

Добавлено через 1 минуту и 17 секунд
итак Вы решили (с чем я не согласен) что синхронность будет удобнее..

Добавлено через 2 минуты и 12 секунд
тогда у нас есть rpc::client, к которому мы можем посылать запросы и получать ответ

Добавлено через 3 минуты и 48 секунд
выполнение запроса все равно будет проходить в паралельном потоке, поэтому мы можем чем нибудь себя занять, но при этом должны уметь контролировать состояние запроса ..

Добавлено через 4 минуты и 44 секунды
тогда при отправке запроса, мы должны получить некую структуру контроллера-наблюдателя..

Добавлено через 8 минут и 32 секунды
допустим это будет выглядить так :

Код

rpc::track<rpc::login> login = client.send ( rpc.login::query("John"));
login.set_timeout (30);
while ( login.in_process() ) do_something ();
if ( ! login().is_ok() )  return;


Добавлено через 9 минут и 45 секунд
отвлекемся на сервер .. 

Автор: mes 9.10.2010, 21:14
сервер хранит модель и список клиентов (сокетов) которые хотят ее изменить.. 
объект сервера выступает своего рода контроллером этих взаимодействий
тогда 
Код

struct server {

   list <client *> clients;
   model_t  model;
 
   void handle (client& , rpc::login);
   void handle (client& .. ); 
  
};


Добавлено @ 21:16
теперь нам необходимо как то зарегистрировать функции 
можно сделать массив/карту делегатов и связать их,

Добавлено @ 21:21
а можно отделить внутренности сервера от методов обработчиков .. 
тогда вместо методов обработчиков определяем таблицу вызовов
и тогда регистриуемая функция будет иметь вид
Код

template<class T>
void () (client&, server_context& , T& packet ) ;


естественно вместо самих функций моно использовать лямбды.. но чтоб не сбиваться идем простым путем.. 

Автор: boostcoder 9.10.2010, 21:27
Цитата(mes @  9.10.2010,  20:58 Найти цитируемый пост)
итак Вы решили (с чем я не согласен) что синхронность будет удобнее..

нет. асинхронность можно реализовать. просто в данный момент мне это не кажется первостепенной задачей.

пишу мысли... минутку..

Автор: mes 9.10.2010, 21:27
итого значит сервер представлен у нас списком клиентом, контекстом/моделью/сессию (что нагляднее) и таблицей "виртуальных " функций.. 

заполнять таблицу и задавать модель должен разработчик, сам сервер должен быть представлен библиотекой,
ну а таблица по идеи генериться кодогенератором ..

Добавлено @ 21:30
итого значит у нас есть rpc::server и rpc::client (не пустать с rpc::server::client ) осуществляющие связь посредством сырых данных и взаимодействующие с пользователем типизированным пакетами.

Добавлено @ 21:36
т.е общий код выглядит так 
Код

// server
rpc::server<model_t> server;
server.register_handler ([](rpc::server<>::client& , model_t&, rpc::login&) // это можно задефайнить
              {       } )

// client
rpc::track<rpc::login> login = client.send ( rpc.login::query("John"));


Добавлено через 9 минут и 58 секунд
писал rpc::login но по сути пакеты должны находится в другом неймспейсе, например название протокола..

Добавлено через 11 минут и 7 секунд
все.. вроде свои мысли высказал..

Автор: boostcoder 10.10.2010, 01:54
вот как я вижу модель:
в данный момент, для каждого входящего соединения(т.к. клиенты сначала подключаются к серверу), создается объект, указанный в типе шаблона сервера. таким образом, получается так, что каждый клиент общается со своим собственным объектом. при завершении сеанса, объект удаляется.
есть еще вариант, инициализировать объект сервер, ссылающимся на общий для всех клиентов объект. в таком случае, так как объект реализует кодер, то синхроннизация/разделение_доступа ложится на него.
Код

type1 t1;
server<type1> server(...);
server.bind(t1);


тогда, в дополнение, было бы мегаудобным, реализовать в классе client<> запрос, типа create_session<type>(). который будет на стороне сервера создавать объект сессии, и переключать контекст клиента на этот объект.
если пойти немного дальше, то класс server<> можно реализовать так, чтоб список шаблонных аргументов был переменным. в таком случае, клиент, может делать запросы на создание какого-то конкретного объекта сессии, и пользоваться им.
Код

server<type1, type2, type3, type4> server(...);

...
...

client client(...);
client::session_ptr<type1> t1ptr = client.create_session<type1>();
// далее, вызываем методы предоставляемые типом type1
...

client::session_ptr<type2> t2ptr = client.create_session<type2>();
// далее, вызываем методы предоставляемые типом type2
...


но это все потом.
сейчас первостепенный вопрос в том, какие данные должен генерировать кодогенератор чтоб клиент и сервер могли автосвязывать запросы/ответы с методами/реализацией?

Автор: mes 10.10.2010, 09:37
Цитата(boostcoder @  10.10.2010,  00:54 Найти цитируемый пост)
в данный момент, для каждого входящего соединения(т.к. клиенты сначала подключаются к серверу), создается объект, указанный в типе шаблона сервера. таким образом, получается так, что каждый клиент общается со своим собственным объектом. при завершении сеанса, объект удаляется.

но насколько я понял, у вас к примеру  есть комнаты, в которые могут зайти разные клиенты, а значит сессия (или ее контекст) не у каждого своя а разделяемая(shared).

Цитата(boostcoder @  10.10.2010,  00:54 Найти цитируемый пост)
если пойти немного дальше, то класс server<> можно реализовать так,

детали реализации действительно лишние.. как удобнее сделать, будет видно при кодировании.. главное разобраться с сутью взаимоотношений.



Цитата(boostcoder @  10.10.2010,  00:54 Найти цитируемый пост)
сейчас первостепенный вопрос в том, какие данные должен генерировать кодогенератор чтоб клиент и сервер могли автосвязывать запросы/ответы с методами/реализацией?

на мой взгляд достаточно только генерации пакетов, и списка типов пакетов. 
клиент и сервер должны быть целыми (не кодогенерируемыми) шаблонами.

Добавлено @ 09:39
список типов определяет какими сообщениями они могут обмениваться, а также для для сервера служит основой для таблицы регистрации..

Добавлено @ 09:44
как выглядит пакет и какие данные он должен содержать зависит от клиента и сервера.. поэтому имхо первостепенный вопрос стоит не в кодогенераторе, а в проектирование конечных точек соединения.. при том для упрoщения можно реализовывать в одной проге и  передавать данные напрямую, как в начале темы через  функции типа  void call(id, void*);

Автор: boostcoder 11.10.2010, 02:36
Цитата(mes @  10.10.2010,  09:37 Найти цитируемый пост)
но насколько я понял, у вас к примеру  есть комнаты, в которые могут зайти разные клиенты, а значит сессия (или ее контекст) не у каждого своя а разделяемая(shared).

сессия - контекст пользователя/соединения.
комнаты - некоторые объекты, взаимодействие с которыми происходит посредством сессий.

Цитата(mes @  10.10.2010,  09:37 Найти цитируемый пост)
на мой взгляд достаточно только генерации пакетов, и списка типов пакетов. 
клиент и сервер должны быть целыми (не кодогенерируемыми) шаблонами.

наверное я вас не понимаю.. поясните пожалуйста.

немного пораздумав.. должно получиться как-то так:
Код

// декларация интерфейса.
INTERFACE_BEGIN(type1)
   METHOD(int, add, int, int);
   METHOD(int, sub, int, int);
INTERFACE_END()

// нужно сгенерировать что-то вроде этого:
// этот код используется только на стороне клиента.
struct type1 {
   int add(int a, int b) {
      static const int function_id = 0; // вставляет кодогенератор
      // пакуем данные..
      serialize(function_id, a, b); // вставляет кодогенератор
      // посылаем, десериализуем, возвращаем результат
      return send(serialized_buffer);
   }
   int sub(int a, int b) {
      static const int function_id = 1; // вставляет кодогенератор
      // пакуем данные
      serialize(function_id, a, b); // вставляет кодогенератор
      // посылаем, десериализуем, возвращаем результат
      return send(serialized_buffer);
   }
private:
   type1(protocol_implementation& pi):pi(pi) {}

private:
   protocol_implementation& pi;
};

// класс клиента:
template<typename IF>
struct client: IF {
   client(host, port):IF(pi),pi(host, port) {}

private:
   protocol_implementation pi;
};

// ну и на клиентской стороне использовать так:
client<type1> client(...);
int res = client.add(2,3);
int res1 = client.sub(res,3);


сервер:
Код

// на стороне сервера, юзер пишет реализацию:
struct type1 {
   int add(int a, int b) {
      return a+b;
   }
   int sub(int a, int b) {
      return a-b;
   }
};
// но, т.к. реализацию пишем мы сами, сервер должен уметь связывать локальный объект с 
// идентификаторами которые использует клиент для обозначения метода используемого для обработки посланного запроса.
// т.е. получается так, что метагенератор должен генерировать какой-то класс, присваивая ему имя типа binder_for_type1.
// но что из себя должен представлять класс binder_for_type1? и какую информацию содержать?


Автор: mes 11.10.2010, 10:42
ловите в упрощенном виде : http://liveworkspace.org/code/bbc75134b93c68f00914597f9c23e38b
с односторонним обменом клиент->сервер

rpc - библиотека конечных точек взаимодействия
ptcl -(кодогенерируемый) протокол обмена
my_server - оболочка реализующая поведение сервера (может быть определена как неймспейсом, так и классом)

ну и в глобальном пространстве условных запуск трех программ, сервера и двух клиентов.. 


Автор: boostcoder 11.10.2010, 10:48
mes, большое спасибо smile 
сейчас буду разбираться.

зы
на liveworkspace работает табуляция в коде. еще Ctrl+G, Ctrl+F, Ctrl+R, Ctrl+Z. оцените smile 

Автор: mes 11.10.2010, 10:50
 smile 
Цитата(boostcoder @  11.10.2010,  09:48 Найти цитируемый пост)
на liveworkspace работает табуляция в коде. еще Ctrl+G, Ctrl+F, Ctrl+R. оцените

даже не ожидал  ! учту, спасибо )

Автор: mes 11.10.2010, 11:10
и я еще раз насчет (а)синхронности...  мне кажется вместо классического вызова функций с получением ответа, где сервер выступает в пассивной форме,
удобнее было бы воспользоваться системой взаимообмена сообщений, тогда клиент будет обладать такой же таблицей методов, как и сервер
а пакеты разделяться на две группы (client_to_server, server_to_client);



Автор: boostcoder 11.10.2010, 11:11
Цитата(mes @ 11.10.2010,  11:10)
и я еще раз насчет (ас)синхронности...  мне кажется вместо классического вызова функций с получением ответа, где сервер выступает в пассивной форме,
удобнее было бы воспользоваться системой взаимообмена сообщений, тогда клиент будет обладать такой же таблицей методов, как и сервер
а пакеты разделяться на две группы (client_to_serever, server_to_client);

да. согласен. это хороший вариант.

Автор: boostcoder 25.10.2010, 23:41
продолжим-с...
что у меня есть сейчас:
Код

struct i_registration_api {
   virtual void user_exists_handler ( user_exists_result&, const user_exists_query& ) = 0;
   virtual void registration_handler ( registration_result&, const registration_query& ) = 0;
   virtual void activation_handler ( activation_result&, const activation_query& ) = 0;
   i_registration_api () {
      _map[0] = i_invoker_ptr( new invoker< i_registration_api, user_exists >(this, &i_registration_api::user_exists_handler ) );
      _map[1] = i_invoker_ptr( new invoker< i_registration_api, registration >(this, &i_registration_api::registration_handler ) );
      _map[2] = i_invoker_ptr( new invoker< i_registration_api, activation >(this, &i_registration_api::activation_handler ) );
   }
   // этот метод дергает сервер. наверное его нужно спрятать от юзера...
   virtual void proxy_call(constants::header_packet& h, constants::body_packet& b) {
      int id = header_coders::decode_header(h).id;
      _map[id]->dispatch(h, b);
   }
private:
   std::map<int, i_invoker_ptr> _map;
};


этот код генерит метагенератор.
юзеру остается унаследоваться от этого класса, и реализовать чистовиртуальные методы.
как видно, для каждой команды свой метод обработчик. не плохо..

недостаток в том, что пока что, сервер строго привязан типом шаблона.
полагаю, сейчас нужно реализовать динамическое создание классов-обработчиков.

идея такая: метегенератору указывать имя метаконтейнера который он должен сгенерить помимо класса. и когда юзер запрашивает создание объекта некоторого типа, сервер в метаконтейнере выискивает этот тип. на этом мои мысли исчерпываются..


mes, вы в теме? smile 

Автор: mes 26.10.2010, 11:28
Цитата(boostcoder @  25.10.2010,  22:41 Найти цитируемый пост)
недостаток в том, что пока что, сервер строго привязан типом шаблона.

не до конца понятно о чем тут.. 

над кодом сейчас подумаю.. но сразу чувствуется, что пока это не то что надо .. 

Автор: boostcoder 26.10.2010, 11:33
Цитата(mes @  26.10.2010,  11:28 Найти цитируемый пост)
не до конца понятно о чем тут..

вот:
Код

struct registration_api: i_registration_api {
// реализуем виртуальные методы
};

server<registration_api> server(...); // все. сервер может работать только с этим типом.

но это плохая реализация. нелепо как-то, в один интерфейс пихать сотни методов smile 

Автор: mes 26.10.2010, 11:38
Цитата(boostcoder @  26.10.2010,  10:33 Найти цитируемый пост)
но это плохая реализация. нелепо как-то, в один интерфейс пихать сотни методов 

понял.. Вы о том что у сервера только один исполнитель...  ага.. это не то.. но по этому поводу у мя вопрос : 
исполнители должны иметь каждый свою группу сообщений или некоторые сообщения будут для исполнителей общие ?



Автор: boostcoder 26.10.2010, 11:51
Цитата(mes @  26.10.2010,  11:38 Найти цитируемый пост)
исполнители должны иметь каждый свою группу сообщений или некоторые сообщения будут для исполнителей общие ?

у каждого свои сообщения + общие.
общие, это:
я хочу реализовать переброс исключений со стороны сервера клиенту. возможно еще какие-то технические данные.

еще момент. проблема с конструкторами исполнителей. каким образом реализовать поддержку аргументов для конструкторов?

Добавлено через 2 минуты и 15 секунд
сейчас приведу структуру классов для понимания..

Добавлено через 12 минут и 20 секунд
Код

template<typename IF>
struct user_context {
   user_context():_impl(new IF) {}
   boost::shared_ptr<IF> _impl;
   // все остальное скрыто.
};

template<typename IF>
struct server {
// по boost::asio::ip::tcp::acceptor::async_accept() создается новый user_context<IF>
};

где IF - тип исполнителя.

Автор: mes 26.10.2010, 12:11
Цитата(boostcoder @  26.10.2010,  10:51 Найти цитируемый пост)
я хочу реализовать переброс исключений со стороны сервера клиенту. возможно еще какие-то технические данные.

опять на помню, может уговорю пересмотреть контракт с сервером с запрос->ответ, на вызов<->вызов
smile


Цитата(boostcoder @  26.10.2010,  10:51 Найти цитируемый пост)
еще момент. проблема с конструкторами исполнителей. каким образом реализовать поддержку аргументов для конструкторов?

оставить создание исполнителя на стороне пользователя..

Добавлено через 42 секунды
как вариант передавать библиотеке ссылку на фабрику..

Добавлено через 3 минуты и 18 секунд
итак для начал нам нужно ограничить проблему.. 
что мы имеем ?
набор некоторых типов данных с ид
набор исполнителей

нам надо доставить данные нужному/нужным исполнителям.. алсо нужно предусмотреть таблицу соответсвий, желательно статическую по отношению к исполнителям.. 


Автор: boostcoder 26.10.2010, 12:18
Цитата(mes @  26.10.2010,  12:11 Найти цитируемый пост)
пересмотреть контракт с сервером с запрос->ответ, на вызов<->вызов

я пока что не представляю как это должно выглядеть/функционировать.. на самом деле, я понимаю, что то, каким образом сейчас происходит общение - это тупик. но не понимаю я как это должно функционировать smile 
поясните плиз ;)

Цитата(mes @  26.10.2010,  12:11 Найти цитируемый пост)
как вариант передавать библиотеке ссылку на фабрику.. 

да, вариант.. набросаю пример..

Автор: mes 26.10.2010, 12:19
Цитата(boostcoder @  26.10.2010,  11:18 Найти цитируемый пост)
да, вариант.. набросаю пример.. 

не торопитесь с этим ..

Добавлено через 12 секунд
Цитата(boostcoder @  26.10.2010,  11:18 Найти цитируемый пост)
поясните плиз ;)

сейчас

Автор: boostcoder 26.10.2010, 12:21
кстати. так же, было бы шикарно, реализовать возможность передавать серверу аргумент для исполнителя при запросе его создания.
Код

client client(...);
client::result_type<registration_api> reg = client.create<registration_api>(arguments...);

 smile 

Автор: mes 26.10.2010, 12:38
Цитата(boostcoder @  26.10.2010,  11:18 Найти цитируемый пост)
не представляю как это должно выглядеть/функционировать..

Ваша схема (если убрать все лишнее) выглядит так :
Код

struct  client;

struct server {
   login_result login (client&) { return .. }
   quit_result  quit (client&)  { return .. }    
};

class client {
   login_result login () { return m_server.login(*this); }
   quit_result quit ()   { return m_server.quit(*this); }  
   
   client(server&s) : m_server(s) {}
   
   server& m_server;
};

главный недостаток в том, что сервер предоставляет информацию ТОЛЬКО по запросу пользователя..
то есть например сообщения чата, будут читаться только когда пользователь запросит, а сервер должен их хранить все это время... 
а не тогда когда кто то написал.. 
 


Автор: xvr 26.10.2010, 12:42
Цитата(mes @  26.10.2010,  12:38 Найти цитируемый пост)
главный недостаток в том, что сервер предоставляет информацию ТОЛЬКО по запросу пользователя
Если посмотреть на классиков (DCOM например), то там для этих случаев используют callback'и. Т.е. по сути локальный сервер на стороне пользователя + локальный пользователь на стороне сервера. 


Автор: mes 26.10.2010, 12:46
а вот так примерно выглядит схема вызов<->вызов : 
Код


struct server {
   void login (client&) {  };
   void quit (client&) { }
   void chat_msg (client& c, std::string& phrase)
   {
        for each(other in all_clients)
        {
           if (other != c)
             client.on_chat_msg(chat_msg_t(c.name, phrase));
        }
   }
};

class client {
   void login ()    { m_server.login(*this); }
   void quit ()     { m_server.quit(*this); }  
   void chat_msg () { m_server.chat_msg(*this, "hallo"); }
   
   void on_login ()  { std::cout << "login ok "; }
   void on_quit  ()  { std::cout << "bye bye "; }
   void on_chat_msg (chat_msg_t m)
   {
     std::cout <<"from " << m.user_name << ": " << m.phrase;   
   }
      
   client(server&s) : m_server(s) {}
   
   server& m_server;
};


Добавлено @ 12:48
Цитата(xvr @  26.10.2010,  11:42 Найти цитируемый пост)
Если посмотреть на классиков (DCOM например), то там для этих случаев используют callback'и.

не знаю как в там в DCOM, но фактически асинхронные callback`и я и предлагаю.. 
 smile 
только вот НЕ с соотношением один вызов один каллбяк )

Автор: boostcoder 26.10.2010, 12:50
что-то не въезжаю.. откуда на стороне клиента может взяться сервер, и на стороне сервера клиент? кто тогда является сервером?

Автор: mes 26.10.2010, 12:51
правда справедливость названия  каллбяков в данном случае условна и зависит от способа рассмотрения..

Добавлено @ 12:52
Цитата(boostcoder @  26.10.2010,  11:50 Найти цитируемый пост)
что-то не въезжаю.. откуда на стороне клиента может взяться сервер, и на стороне сервера клиент? кто тогда является сервером? 

есть локальный сервер и удаленный smile

Добавлено @ 12:52
с клиентом то же самое )

Добавлено @ 12:59
boostcoder, в общем чтоб не путаться
 если информация содержится/накапливается на сервере а клиент ее должен считать, то Ваш способ подходит
если нужна возможность непосредственного взаимодействия клиента с клиентами или ж просто нужна возможность уведомления от сервера в независимости от запроса клиента то не подойдет. 

Автор: xvr 26.10.2010, 13:00
Цитата(mes @  26.10.2010,  12:46 Найти цитируемый пост)
не знаю как в там в DCOM,
Один в один с твоим предложением  smile 

Цитата(boostcoder @  26.10.2010,  12:50 Найти цитируемый пост)
кто тогда является сервером? 
Оба. Равно как и клиентом, тоже оба.


Автор: boostcoder 26.10.2010, 13:06
Цитата(mes @  26.10.2010,  12:51 Найти цитируемый пост)
если нужна возможность непосредственного взаимодействия клиента с клиентами или ж просто нужна возможность уведомления от сервера в независимости от запроса клиента то не подойдет.

да, собирался реализовать обратные колбэки от сервера клиенту. так что нужно.

Цитата(xvr @  26.10.2010,  13:00 Найти цитируемый пост)
Оба. Равно как и клиентом, тоже оба.

т.е. чтоб скомпилить клиентскую сторону, должна быть доступна реализация сервера, и на оборот? я правильно понял?

Автор: mes 26.10.2010, 13:09
Цитата(xvr @  26.10.2010,  12:00 Найти цитируемый пост)
Оба. Равно как и клиентом, тоже оба.

вот например фирма выполняет заказ.. она в этот момент сервер, а заказчик клиент, 
а когда фирма высылает ему счет для оплаты, то заказчик сервер, а фирма клиент smile
т.е. все зависит от темы рассмотрения.. 
smile

Добавлено @ 13:11
Цитата(boostcoder @  26.10.2010,  12:06 Найти цитируемый пост)
т.е. чтоб скомпилить клиентскую сторону, должна быть доступна реализация сервера, и на оборот? я правильно понял? 

не совсем так.. клиентский клиент и клиентский сервер, несколько (а может и совершенно) другие чем серверный сервер и серверный клиент smile
smile

Добавлено @ 13:11
все зависит от исполнения.. smile

Добавлено @ 13:14
boostcoder, вот с точки зрения основной идеи вашей программы, (не знаю что там но допустим чат)
сервер будет сервером вашего чата, а клиент, клиентом вашего чата,
но и тот и другой могут иметь серверы удаленых вызовов.. 

Автор: boostcoder 26.10.2010, 13:26
т.е. мне нужно сейчас разделить реализацию сервера и клиента на две части.
сервер:
1. класс с аксэптором слушающим порт(КАСП).
2. класс взаимодействующий с сокетом клиента(КВСК).

клиент:
1. класс взаимодействующий с сокетом сервера(КВСС).
2. сериализатор/десериализатор команд(СДК).

мысли в слух..
на стороне сервера создаем объект КАСП и СДК. КАСП используется по назначению, в то время, как СДК используется для реализации обратных вызовов клиента.
на стороне клиента создаем объект КВСС и КВСК. КВСС используется по назначению, в то время, как КВСК используется 

запутался..

Добавлено через 55 секунд
в таком случае, нужно два соединения? одно клиента-с-сервером, второе сервера-с-клиентом? так?

Автор: mes 26.10.2010, 13:29
Цитата(boostcoder @  26.10.2010,  12:26 Найти цитируемый пост)
одно клиента-с-сервером, второе сервера-с-клиентом? так? 

сокет-соединение ? нет одно .. 
остальное еще не прочел

Добавлено @ 13:33
Цитата(boostcoder @  26.10.2010,  12:26 Найти цитируемый пост)
сейчас разделить реализацию сервера и клиента на две части.

я б сказал не так :
есть сокет соединение 
есть rpc соединение 
и само исполнение

естессвенно у каждого модуля(сервер или клинт) своя реализация каждого уровня.. общие только библиотеки.. 

Автор: boostcoder 26.10.2010, 13:53
..не понимаю..

может обратные вызовы реализовать следующим образом:
у клиента добавить метод типа "set_callback(int id, function)", где id - идентификатор колбэка, function - объект указывающий на функцию/метод на стороне клиента который будет вызываться. т.е. при вызове set_callback() указанный идентификатор регистрируется на сервере. в то время, на сервере добавить метод типа "callback(id, args...)". где id - идентификатор вызываемого на стороне клиента метода. клиент в свою очередь, должен уметь отличать ответы сервера на свои запросы, от вызовов колбэков.

как такая идея?

Автор: mes 26.10.2010, 14:39
Цитата(boostcoder @  26.10.2010,  12:53 Найти цитируемый пост)
как такая идея? 

и так у нас есть как клиент так и сервер могут отправлять вызовы
т.е. каждый из них является то отправителем то получателем.. 

поставленная задача:  распределять сообщения по исполнителям..
на тек момент одно сообщение, один исполнитель..
предполагается, что набор исполнителей представляют собой один композиционный объект
допустим есть такие сообщения :
Код

struct notice { enum { id = 0 }; };
struct login  { enum { id = 1 }; };
struct quit   { enum { id = 2 }; };
struct ping   { enum { id = 3 }; };
struct pong   { enum { id = 4 }; };

такой набор исполнителей 
Код

struct auth_handler {
     void on_login  (login const&)  { std::cout << ".auth.login" << std::endl; }
     void on_quit   (quit  const&)  { std::cout << ".auth.quit" << std::endl;  }
};

struct pingpong_handler
{
     void on_ping   (ping const&)   { std::cout << ".pp.ping" << std::endl; }
     void on_pong   (pong const&)   { std::cout << ".pp.pong" << std::endl; }
};

struct recipient
{
   void on_notice (notice const&) { std::cout << ".notice" << std::endl; }
   
   auth_handler       auth;
   pingpong_handler   pingpong;
      
};

диспетчера сообщений :
Код

struct idispatcher
{
   virtual void dispatch (recipient& r, void const* raw_msg )=0;
};

#define DEF_DISP(name, typ, func) \
struct name : idispatcher \
{ \
    virtual void dispatch (recipient& r, void const* raw_msg ) \
    { \
       r func (*static_cast<typ const*>(raw_msg)); \
    } \
};
DEF_DISP(login_dispatcher,  login,  .auth.on_login );
DEF_DISP(quit_dispatcher,   quit,   .auth.on_quit );
DEF_DISP(ping_dispatcher,   ping,   .pingpong.on_ping );
DEF_DISP(pong_dispatcher,   pong,   .pingpong.on_pong );
DEF_DISP(notice_dispatcher, notice, .on_notice );


условный сервер приемки сообщений
Код

idispatcher * disp_table[5] = 
{
  new notice_dispatcher,
  new login_dispatcher,
  new quit_dispatcher,
  new ping_dispatcher,
  new pong_dispatcher
};

void dispatch (size_t msg_id, void const* raw_msg )
{
     disp_table[msg_id]->dispatch(g_recipient, raw_msg);
}

отправитель :
Код

template<class T>
void send (T const& msg)
{
    dispatch (T::id, &msg);
}


тестовый пример :
Код

int main ()
{
    send (login());
    send (ping());
    send (pong());    
    send (notice());
    send (quit());
}

и вывод
Цитата

.auth.login
.pp.ping
.pp.pong
.notice
.auth.quit


http://liveworkspace.org/code/5afd22007254aa466ad429549e89fe69

Добавлено через 1 минуту
сейчас попробую доработать, чтоб одно сообщение могло в нескольких местах отрабатывать..

Автор: mes 26.10.2010, 15:05
Цитата(mes @  26.10.2010,  13:39 Найти цитируемый пост)
сейчас попробую доработать, чтоб одно сообщение могло в нескольких местах отрабатывать..

чего то заклинил, можно ведь  вместо карты(массива) диспетчеров просто сделать мультикарту.. 
smile

Автор: boostcoder 26.10.2010, 15:23
Цитата(mes @  26.10.2010,  14:39 Найти цитируемый пост)
и так у нас есть как клиент так и сервер могут отправлять вызовы
т.е. каждый из них является то отправителем то получателем.. 

знаков препинания не хватает..смысла не понял...

Добавлено через 25 секунд
Цитата(mes @  26.10.2010,  15:05 Найти цитируемый пост)
можно ведь  вместо карты(массива) диспетчеров просто сделать мультикарту.. 

ага

Добавлено через 55 секунд
по остальному - вникаю в суть...

Автор: mes 26.10.2010, 15:27
Цитата(boostcoder @  26.10.2010,  14:23 Найти цитируемый пост)
знаков препинания не хватает..смысла не понял...


Цитата(mes @  26.10.2010,  13:39 Найти цитируемый пост)
и так у нас есть:
 как клиент, так и сервер могут отправлять вызовы, т.е. каждый из них является то отправителем, то получателем.. 


Добавлено через 1 минуту и 35 секунд
т.е. приведенный код будет как на севере, так и на клиенте, только для своих групп сообщений

Автор: boostcoder 26.10.2010, 15:44
Цитата(mes @ 26.10.2010,  15:27)
Цитата(boostcoder @  26.10.2010,  14:23 Найти цитируемый пост)
знаков препинания не хватает..смысла не понял...


Цитата(mes @  26.10.2010,  13:39 Найти цитируемый пост)
и так у нас есть:
 как клиент, так и сервер могут отправлять вызовы, т.е. каждый из них является то отправителем, то получателем.. 


Добавлено @ 15:28
т.е. приведенный код будет как на севере, так и на клиенте, только для своих групп сообщений

ага. понял... щас..

Автор: mes 26.10.2010, 15:52
Цитата(mes @  26.10.2010,  14:05 Найти цитируемый пост)
можно ведь  вместо карты(массива) диспетчеров просто сделать мультикарту.. 

или отделить функциональный вызов от диспатчеризации
т.е. вместо макроса DEF_DISP и всего с его участием, пишем что то типа такого :

Код

// библиотечная часть
template<class T>
struct dispatcher : idispatcher

    virtual void dispatch (recipient& r, void const* raw_msg ) 
    { 
       func (r, *static_cast<typ const*>(raw_msg)); 
    }
    dispatcher (..)..
    boost::function<void (*) (recipient&, T const& msg)> func;
};

Код

// пользовательская
register ( dispather<login> 
    ([] (recipient& r, login& msg ) 
    {   
         r      .on_notice(msg);
         r.auth .on_notice (msg);
         ...
    }) 
);

могут быть ошибки.. с лямбдами пока еще не приходилось работать.. 

Автор: mes 26.10.2010, 18:01
вот доработал немного :
http://liveworkspace.org/code/17eb2d53ecbbf51755b334c82dbf190e

Добавлено @ 18:11
а вот чистый пример, без классов исполнителей..  
http://liveworkspace.org/code/6e7e20117c8516fd84e7a337ac958087

Добавлено @ 18:13
вот пока объяснял сам разобрался как происходить должно... 
smile 

если что осталось непонятным, спрашивайте 
smile


Автор: boostcoder 26.10.2010, 19:50
последними двумя примерами, вы меня окончательно запутали..
я так понял, что сейчас мы решаем ассоциирование команд с разными интерфейсами? или что?
до этого, я думал что сейчас мы решаем как создавать исполнителей на стороне сервера по запросу клиента.
а до этого, думал что мы решали каким образом реализовать колбэки.
и это все за один день smile 

Автор: mes 26.10.2010, 20:20
Цитата(boostcoder @  26.10.2010,  18:50 Найти цитируемый пост)
последними двумя примерами, вы меня окончательно запутали..

так по сути там то же самое.. просто лишние детали выкорчивались.. 


Цитата(boostcoder @  26.10.2010,  18:50 Найти цитируемый пост)
я так понял, что сейчас мы решаем ассоциирование команд с разными интерфейсами? или что?

да вначале решалась задача как доставить сообщение конкретному исполнителю.. 
потом встала цель перенаправления одного сообщения нескольким исполнетелям,
после чего обнаружилось, что исполнителей как таковых может не быть, как частный случай реализации..
ну а в последнем убрал контекст исполнителей чтоб выделить суть.

Добавлено через 1 минуту и 9 секунд
Цитата(boostcoder @  26.10.2010,  18:50 Найти цитируемый пост)
до этого, я думал что сейчас мы решаем как создавать исполнителей на стороне сервера по запросу клиента.

исполнители, это пользовательская часть и серверу она если и нужна, то только для связки с сообщениями..

Добавлено через 1 минуту и 49 секунд
Цитата(boostcoder @  26.10.2010,  18:50 Найти цитируемый пост)
а до этого, думал что мы решали каким образом реализовать колбэки.

и это то же.. как подзадачу smile

Добавлено через 3 минуты и 24 секунды
сейчас надо определиться с какой стороны идти.. 

Автор: boostcoder 26.10.2010, 20:28
mes, я вам очень благодарен..но дело в том, что я и в правду запутался.
если вас не затруднит, опишите пожалуйста, какой вы видите реализацию, словами. для дурака так сказать.


очень благодарен вам за то что вы на протяжении всей темы помогаете мне. спасибо.


Автор: mes 26.10.2010, 20:34
Цитата(boostcoder @  26.10.2010,  19:28 Найти цитируемый пост)
.но дело в том, что я и в правду запутался.

это я понял.. поэтому и написал :
Цитата(mes @  26.10.2010,  19:20 Найти цитируемый пост)
сейчас надо определиться с какой стороны идти..  

вот сижу и думаю, с чего начать, что у вас уже есть, и где возникла  путаница...

Добавлено @ 20:42
ну например клиент может выглядить так :
Код

rpc::socket_connection<my_protocol> connection;
connection.register ([](login_result const& msg){ std::cout <<(msg.ok ?  "login ok  " : "login fail") << std::endl; });
connection << login("boostcoder", "pass");

тут есть вопросы ?

пояснения :
my_protocol  это traits- структура, содержащая список сообщений, допустимых для передачи по соединению.. 
register() - установка делегата
оператор <<  - отправка сообщения в сеть




Цитата(boostcoder @  26.10.2010,  19:28 Найти цитируемый пост)
вы на протяжении всей темы 

у меня сейчас похожая задача решается.. так что получилось, что не только Вам, но и себе опыта набираю.. 

smile

Автор: boostcoder 26.10.2010, 20:49
сейчас есть рабочая реализация сервера и клиента, позволяющая декларировать в удобном(абстрактном) для пользователя синтаксисе команды запросов/ответов + генератор всей подноготной необходимой серверу чтоб знать, какая команда с каким обработчиком ассоциирована.

первым и очевидным недостатком текущей реализации является то, что сервер привязан к конкретному классу обработчиков. и не имеет возможности в рантайме их менять/создавать.
второй недостаток, это то, что нет нормальной реализации обратных колбэков.

приоритеты возможно не в том порядке, т.к. возможно одно зависит от другого.

вот.

Автор: mes 26.10.2010, 20:51
Цитата(boostcoder @  26.10.2010,  19:49 Найти цитируемый пост)
сейчас есть рабочая реализация сервера и клиента, позволяющая декларировать в удобном(абстрактном) для пользователя синтаксисе команды запросов/ответов + генератор всей подноготной необходимой серверу чтоб знать, какая команда с каким обработчиком ассоциирована.


ну для начала нам достаточно одной, двух комманд чтоб проверить в действии. а уж потом, стоит это поднаготную запаковывать smile

Добавлено через 44 секунды
Цитата(boostcoder @  26.10.2010,  19:49 Найти цитируемый пост)
первым и очевидным недостатком текущей реализации является то, что сервер привязан к конкретному классу обработчиков. и не имеет возможности в рантайме их менять/создавать.

т.е. с приемом/передачей одного сообщения у вас проблем нет ?

Автор: boostcoder 26.10.2010, 20:58
под обработчиком подразумеваю это:
Код

struct i_registration_api {
   virtual void user_exists_handler ( user_exists_result&, const user_exists_query& ) = 0;
   virtual void registration_handler ( registration_result&, const registration_query& ) = 0;
   virtual void activation_handler ( activation_result&, const activation_query& ) = 0;
   i_registration_api () {
      _map[0] = i_invoker_ptr( new invoker< i_registration_api, user_exists >(this, &i_registration_api::user_exists_handler ) );
      _map[1] = i_invoker_ptr( new invoker< i_registration_api, registration >(this, &i_registration_api::registration_handler ) );
      _map[2] = i_invoker_ptr( new invoker< i_registration_api, activation >(this, &i_registration_api::activation_handler ) );
   }
   // этот метод дергает сервер. наверное его нужно спрятать от юзера...
   virtual void proxy_call(constants::header_packet& h, constants::body_packet& b) {
      int id = header_coders::decode_header(h).id;
      _map[id]->dispatch(h, b);
   }
private:
   std::map<int, i_invoker_ptr> _map;
};

т.е. это конечный код генерируемый метагенератором.

на стороне сервера используется так:
Код

struct registration_api: i_registration_api {
   // реализуем виртуальные методы
   // т.е. юзеру ничего более о внутренностях реализации знать не надо.
   // он всего лишь должен реализовать чистовиртуальные методы. все.
};

server<registration_api> server(...); // все. сервер может работать только с этим типом.


на стороне клиента:
Код

client client(argv[1], std::atoi(argv[2]));

user_exists::result_type ret = client.query<user_exists>("John", "[email protected]");
std::cout << ret->message() << ", " << ret->code() << std::endl;


вот.
вроде ничего не упустил.

Добавлено @ 21:01
Цитата(mes @  26.10.2010,  20:51 Найти цитируемый пост)
т.е. с приемом/передачей одного сообщения у вас проблем нет ?

не только одного. многих.
вот реальный пример теста:
Код

      client client(argv[1], std::atoi(argv[2]));
      while ( true ) {
         user_exists::result_type ret = client.query<user_exists>("John", "[email protected]");
         std::cout << ret->message() << ", " << ret->code() << std::endl;

         registration::result_type ret1 = client.query<registration>("John", "[email protected]");
         std::cout << ret1->message() << ", " << ret1->code() << std::endl;

         activation::result_type ret2 = client.query<activation>("John", "234566534");
         std::cout << ret2->message() << ", " << ret2->code() << std::endl;

      }


зы
у меня есть приватное SVN хранилище, может быть дать вам доступ? это значительно облегчит понимание.

Автор: mes 26.10.2010, 21:09
вот опять вернулись к самому началу.. вы все время избегаете этого факта..
для создания синхроного запрос/ответ вы идете на искусственные ограничения... 
если  нужна будет синхронность ее лучше организовать внешним слоем...

я все ж настаиваю на системе вызов<->вызов, так как с каждой минутой все более уверен, что именно она вам нужна..

мне кажется что Вам просто жаль усилий уже потраченных на кодогенератор, поэтому не хотите взглянуть по сторонам.. 

Добавлено через 3 минуты и 18 секунд
Цитата(boostcoder @  26.10.2010,  19:58 Найти цитируемый пост)
не только одного. многих.

для начала все ж дождусь ответа, готовы ли следовать предложеной схеме и отказаться от своей ? 


Автор: boostcoder 26.10.2010, 21:14
Цитата(mes @  26.10.2010,  21:09 Найти цитируемый пост)
я все ж настаиваю на системе вызов<->вызов, так как с каждой минутой все более уверен, что именно она вам нужна..

я ведь не спорю. просто я не могу понять что именно вы подразумеваете под этим. как это должно выглядеть? потому я писал выше, что запутался в задачах. что сейчас нужно изменить в уже имеющемся коде?

да нет же. в данный момент, я именно в ступоре. даже не могу понять что делать дальше.

Добавлено через 50 секунд
Цитата(mes @  26.10.2010,  21:09 Найти цитируемый пост)
готовы ли следовать предложеной схеме и отказаться от своей ? 

да. просто объясните, что и как должно быть?

Автор: mes 26.10.2010, 21:16
у вас есть возможность проверять и клиент и сервер ? 
то есть как я понял у Вас есть (если все лишнее выкинуть) фактически пустые клиент и сервер на основе сокетов ?

Добавлено @ 21:16
задаю вопросы по частям чтоб не уйти в сторону.. 

Автор: boostcoder 26.10.2010, 21:19
Цитата(mes @  26.10.2010,  21:16 Найти цитируемый пост)
у вас есть возможность проверять и клиент и сервер ? 
то есть как я понял у Вас есть (если все выкинуть) фактически пустые клиент и сервер на основе сокетов ?

да.
сейчас это пустой каркас. для тестов, обработчики заполнил хламом:
Код

struct registration_api: i_registration_api {
   virtual void user_exists_handler(user_exists_result& r, const user_exists_query&) {
      r.code = rand()%33;
      r.message = "yes, John already registered " + boost::lexical_cast<std::string>(rand()%13) + " times";
   }

   virtual void registration_handler(registration_result& r, const registration_query&) {
      r.code = rand()%7;
      r.message = "you succesfuly registered";
   }

   virtual void activation_handler(activation_result& r, const activation_query&) {
      r.code = rand()%3;
      r.message = "please check your e-mail";
   }
};


Автор: mes 26.10.2010, 21:28
Цитата(boostcoder @  26.10.2010,  20:19 Найти цитируемый пост)
сейчас это пустой каркас. для тестов, обработчики заполнил хламом:

выкиньте хлам.. 

добавьте две структуры query и result с разными id
сделайте два сендера один может отправлять query в сокет, другой result
а также два простых получателя, (для каждого по одному сообщению) печатающих допустим id
и разместите соответсвенно одну пару на клиенте , другую на сервере..

ничего лишнего, только самое необходимое.. 

жду (результата или вопросов) smile


Автор: boostcoder 26.10.2010, 21:40
Цитата(mes @  26.10.2010,  21:28 Найти цитируемый пост)
выкиньте хлам.. 

т.е. из тела обработчиков? сами обработчики оставить пустыми?

Цитата(mes @  26.10.2010,  21:28 Найти цитируемый пост)
добавьте две структуры query и result с разными id

в данный момент, структуры(запросы и ответы) генерируются кодогенератором.
так:
Код

DECLARE_HANDLER_CLASS(
   // abstract class name
   registration_api,
   // check if user with this nick and email already exists
   ((user_exists, // это команда
      ((query, // это тип запроса
        ((std::string, nick))
        ((std::string, email))
      ))
      ((result, // это тип ответа
         ((int, code))
         ((std::string, message))
      ))
   ))
   // registration query
   ((registration, // это команда
      ((query,
         ((std::string, nick))
         ((std::string, email))
      ))
      ((result,
         ((std::string, message))
         ((int, code))
      ))
   ))
   // activation query
   ((activation, // это команда
      ((query,
         ((std::string, nick))
         ((std::string, code))
      ))
      ((result,
         ((std::string, message))
         ((int, code))
      ))
   ))
)


т.е. код который вы видите выше, генерируется этим макросом.
т.е. изменить этот макрос так, чтоб в нем были две структуры? или две команды? команда - состоит из двух структур: 1)запрос, 2)ответ.
так как?

Цитата(mes @  26.10.2010,  21:28 Найти цитируемый пост)
сделайте два сендера один может отправлять query в сокет, другой result

на стороне клиента?
т.е. создать два объекта типа client?
или как?

Цитата(mes @  26.10.2010,  21:28 Найти цитируемый пост)
и размести соответсвенно одну пару на клиенте , другую на сервере..

т.е. на сервере один сендер и один получатель? на клиенте так же?

Автор: mes 26.10.2010, 21:55
Цитата(boostcoder @  26.10.2010,  20:40 Найти цитируемый пост)
т.е. из тела обработчиков? сами обработчики оставить пустыми?

без каких либо обработчиков и без применения всего мудренного
5ти строчные объекты.. сейчас времени нет, чуть позже напишу...

Добавлено через 31 секунду
Цитата(boostcoder @  26.10.2010,  20:40 Найти цитируемый пост)
т.е. на сервере один сендер и один получатель? на клиенте так же?

ага smile

Автор: mes 26.10.2010, 22:31
можно даже не два объекта, а две функции 
вот к примеру :
Код

//client
struct connection_t : socket_evthandler 
{
     template<class T>
     void send ( T const&);
  protected:
      ...
     void receive (..)
     {
          ..
         std::cout << "msg_id" << ..
     }
 private:
  socket_t     socket;
};

терь нужно оживить это...

на клиенте и на севере подобная конструкция 
чтоб клиент отправлял серверу query, а тот в ответ result.. 

и еще раз, никаких макросов и кодогенераторов на этом этапе.. 

Автор: boostcoder 26.10.2010, 22:40
ок. понял. сделаю.

Добавлено через 9 минут и 29 секунд
чтение/запись сделать асинхронными?

Автор: boostcoder 26.10.2010, 22:55
если я все верно понял, то вы хотите реализовать запись и чтение в один сокет на каждой стороне? это получится что-то вроде RS-триггера на каждой стороне. т.е. когда клиент записывает в сокет, на стороне сервера получаем событие записи и читаем из сокета. в то время, если сервер пишет, то событие записи получаем на стороне клиента.
все верно?

Добавлено через 52 секунды
идея smile 

Автор: mes 26.10.2010, 23:03
Цитата(boostcoder @  26.10.2010,  21:40 Найти цитируемый пост)
чтение/запись сделать асинхронными? 

ага smile

Цитата(boostcoder @  26.10.2010,  21:55 Найти цитируемый пост)
если я все верно понял, то вы хотите реализовать запись и чтение в один сокет на каждой стороне?

оказывается проблема сидела глубже чем я думал.. smile я думал что изначально это предполагается.. 

Цитата(boostcoder @  26.10.2010,  21:55 Найти цитируемый пост)
 т.е. когда клиент записывает в сокет, на стороне сервера получаем событие записи и читаем из сокета. в то время, если сервер пишет, то событие записи получаем на стороне клиента.

ага smile
я даже боюсь представить как у вас было раньше.. smile


Автор: boostcoder 26.10.2010, 23:11
Цитата(mes @  26.10.2010,  23:03 Найти цитируемый пост)
я даже боюсь представить как у вас было раньше..

 smile 
сервер создавал сокет при подключении и ждал записи со стороны клиента. клиент, после того как записал серверу, ожидал записи со стороны сервера smile 

Автор: mes 26.10.2010, 23:21
Цитата(boostcoder @  26.10.2010,  22:11 Найти цитируемый пост)
сервер создавал сокет при подключении и ждал записи со стороны клиента. клиент, после того как записал серверу, ожидал записи со стороны сервера   

ну в принципе я так и подумал.. просто не смог представить, как вы даже чат на таком реализовывать будете..
( а по видимым кускам, там нечто по сложнее чем чистый чат) smile

зато хоть  все стало становиться на свои места... понятно что вставляло палки в колеса понимания smile


Автор: boostcoder 26.10.2010, 23:25
да..с проектированием у меня есть проблемы. исправляюсь. читаю умные книжки.
наверное еще пол часика и будет код.

Автор: boostcoder 27.10.2010, 02:33
вот.
даже работает smile 
честно признаюсь, я догадывался что оно должно работать именно так, потому и создал на днях http://forum.vingrad.ru/forum/topic-312737.html тему. но что-то меня отвлекло..

сервер:
Код

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

const int bufsize = 1024;
typedef boost::array<char, bufsize> buffer;

/***************************************************************************/

struct server {
   server(boost::asio::io_service& ios, boost::uint16_t port)
      :_acceptor(ios, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
      _socket(ios),
      _idx(0)
   {
      _acceptor.async_accept(_socket,
         boost::bind(&server::handle_accept, this, boost::asio::placeholders::error)
      );
   }

   ~server() {
      std::cout << "destructing server" << std::endl;
   }

   void handle_accept(const boost::system::error_code& e) {
      if ( !e ) {
         std::cout << "server: incoming connection from: " << _socket.remote_endpoint().address().to_string() << std::endl;
         start_read();
         start_write();
      } else {
         std::cout << "server: error on server accept" << std::endl;
      }
   }

   void start_read() {
      boost::asio::async_read(_socket,
         boost::asio::buffer(_inbuffer, _inbuffer.size()),
         boost::bind(&server::read_handler, this, boost::asio::placeholders::error)
      );
   }

   void start_write() {
      sprintf(_outbuffer.begin(), "%d", _idx++);
      boost::asio::async_write(_socket,
         boost::asio::buffer(_outbuffer, _outbuffer.size()),
         boost::bind(&server::write_handler, this, boost::asio::placeholders::error)
      );
   }

   void read_handler(const boost::system::error_code& e) {
      if ( !e ) {
         std::cout << "server: read message form client \"" << _inbuffer.begin() << "\"" << std::endl;
      } else {
         std::cout << "server: read from client error" << std::endl;
      }
      start_read();
   }

   void write_handler(const boost::system::error_code& e) {
      if ( !e ) {
         std::cout << "server: message writen to client \"" << _outbuffer.begin() << "\"" << std::endl;
      } else {
         std::cout << "server: error on writing to client" << std::endl;
      }
      start_write();
   }

private:
   boost::asio::ip::tcp::acceptor _acceptor;
   boost::asio::ip::tcp::socket _socket;
   int _idx;
   buffer _inbuffer;
   buffer _outbuffer;
};

/***************************************************************************/

int main() {
   try {
      boost::asio::io_service ios;
      server server(ios, 44550);
      ios.run();
   } catch (const std::exception& e) {
      std::cout << "(exception): " << e.what() << std::endl;
   }
}

/***************************************************************************/


собирать так:
Цитата

g++ -oserver server.cpp -lboost_system


клиент:
Код

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

const int bufsize = 1024;
typedef boost::array<char, bufsize> buffer;

/***************************************************************************/
struct client {
   client(boost::asio::io_service& ios, const char* ip, boost::uint16_t port)
      :_socket(ios),
      _idx(0)
   {
      boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(ip), port);
      _socket.connect(endpoint);
      std::cout << "client: connected to server" << std::endl;
      /**  */
      start_write();
      start_read();
   }

   ~client() {
      _socket.close();
      std::cout << "destructing client" << std::endl;
   }

   void start_write() {
      sprintf(_outbuffer.begin(), "%d", _idx++);
      boost::asio::async_write(_socket,
         boost::asio::buffer(_outbuffer, _outbuffer.size()),
         boost::bind(&client::write_handler, this, boost::asio::placeholders::error)
      );
   }

   void start_read() {
      boost::asio::async_read(_socket,
         boost::asio::buffer(_inbuffer, _inbuffer.size()),
         boost::bind(&client::read_handler, this, boost::asio::placeholders::error)
      );
   }

   void write_handler(const boost::system::error_code& e) {
      if ( !e ) {
         std::cout << "client: message writen to server \"" << _outbuffer.begin() << "\"" << std::endl;
      } else {
         std::cout << "client: error on writing to server" << std::endl;
      }
      start_write();
   }

   void read_handler(const boost::system::error_code& e) {
      if ( !e ) {
         std::cout << "client: message read from server \"" << _inbuffer.begin() << "\""<< std::endl;
      } else {
         std::cout << "client: error read from server" << std::endl;
      }
      start_read();
   }

private:
   boost::asio::ip::tcp::socket _socket;
   int _idx;
   buffer _inbuffer;
   buffer _outbuffer;
};

/***************************************************************************/

int main() {
   try {
      boost::asio::io_service ios;
      client client(ios, "127.0.0.1", 44550);
      ios.run();
   } catch (const std::exception& e) {
      std::cout << "(exception): " << e.what() << std::endl;
   }
}

/***************************************************************************/


собирать так:
Цитата

g++ -oclient client.cpp -lboost_system

Автор: mes 27.10.2010, 08:51
Цитата(boostcoder @  27.10.2010,  01:33 Найти цитируемый пост)
вот.
даже работает  

 smile 

итак старт положен..  "сырые" данные можете отправлять.. чтоб не путаться назовем объекты этого уровня net_client и net_server..

теперь создаем еще одного clienta, который будет посредством net_client`a отправлять уже типизированные сообщения.. 
и еще один сервер, который на основе net_servera будет по id в карте вызывать нужный диспетчер
для начала диспетчер такого вида :
Код

  struct dispatcher { void dispatch () { std::cout <<"dispatched"; } };

Автор: boostcoder 27.10.2010, 15:59
в следующем варианте, и клиент и сервер, изначально должны находится в режиме чтения. так?

зы
вот теперь я точно понял идею )

Автор: mes 27.10.2010, 16:25
Цитата(boostcoder @  27.10.2010,  14:59 Найти цитируемый пост)
в следующем варианте, и клиент и сервер, изначально должны находится в режиме чтения. так?

речь идет о сокетах ? вообще то должны быть в режиме ожидания, готовые к двусторонней передаче.. т.е как для записи, так и для чтения.. 
( я говорю с точки зрения пользователя сокета.. как там внутри я не  знаю )

Автор: boostcoder 27.10.2010, 16:29
т.е. после подключения, клиент и сервер становятся в операцию асинхронного чтения на сокете? полагаю, так логичнее. т.к. изначально мы не зависим от того, кто ведущий а кто ведомый. и любой из них может первым послать пакет.

Автор: mes 27.10.2010, 16:30
Цитата(mes @  27.10.2010,  07:51 Найти цитируемый пост)
и еще один сервер, который на основе net_servera будет по id в карте вызывать нужный диспетчер

вобще-то это уже будет не сервер..а сессия (или удаленный client.. ) но так как у нас (насколько я понял) сервер на одного клиента, то пока пойдет, потом будет виднее где подправить smile

Добавлено @ 16:31
Цитата(boostcoder @  27.10.2010,  15:29 Найти цитируемый пост)
и любой из них может первым послать пакет. 

 smile 

я был против выражения "установлен на чтение", так как тогда логически  не возможна запись.. но если не воспринимать буквально, то все верно 
smile

Добавлено через 3 минуты и 9 секунд
Цитата(boostcoder @  27.10.2010,  15:29 Найти цитируемый пост)
 т.к. изначально мы не зависим от того, кто ведущий а кто ведомый

значит все таки столько страниц темы были не напрасны  smile 

Автор: boostcoder 27.10.2010, 16:34
к примеру: клиент подключился к серверу. оба читают из сокета.
сервер производит запись. но т.к. клиент ожидает записи в сокет, он читает пакет, снова становится в режим чтения, что-то выполняет с прочитанным пакетом.
так?

еще любопытно, каким образом можно реализовать пакеты двух типов? т.е. пакеты на которые нужно посылать ответ, и пакеты без ответов?

Добавлено через 7 минут и 38 секунд
Цитата(mes @  27.10.2010,  16:30 Найти цитируемый пост)
значит все таки столько страниц темы были не напрасны

 smile 

Автор: mes 27.10.2010, 16:50
Цитата(boostcoder @  27.10.2010,  15:34 Найти цитируемый пост)
к примеру: клиент подключился к серверу. оба читают из сокета.
сервер производит запись. но т.к. клиент ожидает записи в сокет, он читает пакет, снова становится в режим чтения, что-то выполняет с прочитанным пакетом.
так?

висят себе два сокета никого не трогют, в один написали, друг подал сигнал, и его прочитали.. 
smile

Добавлено через 30 секунд
Цитата(boostcoder @  27.10.2010,  15:34 Найти цитируемый пост)
еще любопытно, каким образом можно реализовать пакеты двух типов? т.е. пакеты на которые нужно посылать ответ, и пакеты без ответов?

никаким образом этого не нужно ( и не придется) делать smile

Автор: boostcoder 27.10.2010, 16:53
Цитата(mes @  27.10.2010,  16:50 Найти цитируемый пост)
висят себе два сокета никого не трогют, в один написали, друг подал сигнал, и его прочитали.. 

ну да.

Цитата(mes @  27.10.2010,  16:50 Найти цитируемый пост)
никаким образом этого не нужно ( и не придется) делать

ок.

сейчас код выдам.

Автор: boostcoder 28.10.2010, 00:21
если я все правильно понял, то вот.
код компилиться, но не завершен, т.к. некоторые моменты не ясны.
а именно:
1. клиент, при посылке команды, каким образом будет знать, на какую команду ждать ответа, а на какую нет?
2. серверу, чтоб послать ответ на какую-то команду, так же как и клиенту нужно знать, на какую команду отвечать, а на какую нет.

хидер:
Код


#include <iostream>
#include <memory>
#include <functional>

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/array.hpp>
#include <boost/lexical_cast.hpp>

/***************************************************************************/

static const boost::uint16_t  port = 44550;
static const char*            host = "127.0.0.1";

/***************************************************************************/

struct constants {
   enum { header_length = 12 };
   enum { body_length = 1024*32 };
   enum { client_server, server_client };
   typedef boost::array<char, header_length> header_buffer;
   typedef boost::array<char, body_length> body_buffer;
};

/***************************************************************************/

struct header_coders {
   struct header {
      boost::int32_t    id;
      boost::uint32_t   direction;
      boost::uint32_t   body_length;
   };

   template<typename T>
   static constants::header_buffer encode_header(const std::string& b) {
      constants::header_buffer header;
      sprintf(header.data(), "%04d%02d%06d", T::meta_id, T::meta_dir, b.length());
      return header;
   }

   static header decode_header(const constants::header_buffer& data) {
      header hdr;
      sscanf(data.data(), "%04d%02d%06d", &hdr.id, &hdr.direction, &hdr.body_length);
      return hdr;
   }
};

/***************************************************************************/

inline std::ostream& operator<< (std::ostream& os, const header_coders::header& hdr) {
   os << "id:" << hdr.id << ", dir:" << hdr.direction << ", len:" << hdr.body_length << std::endl;
   return os;
}

/***************************************************************************/

struct notice { enum { id = 0 }; };
struct login  { enum { id = 1 }; };
struct quit   { enum { id = 2 }; };
struct ping   { enum { id = 3 }; };
struct pong   { enum { id = 4 }; };

/***************************************************************************/

struct i_invoker {
   virtual void dispatch(constants::header_buffer&, constants::body_buffer&) = 0;
};
typedef std::shared_ptr<i_invoker> i_invoker_ptr;

template<typename T, typename C>
struct invoker: i_invoker {
   typedef void(C::*handler_t)(T&);

   invoker(C* p, handler_t h):_this(p),_handler(h) {}

   virtual void dispatch(constants::header_buffer& h, constants::body_buffer& b) {
      T data;
      deserializer(data, h, b);
      (*_this.*_handler)(data);
      serializer(h, b, data);
   }

private:
   void deserializer(T& t, const constants::header_buffer&, const constants::body_buffer&) {
      t = T();
   }

   void serializer(constants::header_buffer&, constants::body_buffer&, const T&) {
   }

private:
   C* _this;
   handler_t _handler;
};

/***************************************************************************/

struct i_implementation {
   virtual void notice_handler(notice&) = 0;
   virtual void login_handler(login&) = 0;
   virtual void quit_handler(quit&) = 0;
   virtual void ping_handler(ping&) = 0;
   virtual void pong_handler(pong&) = 0;

   i_implementation() {
      _map[0] = i_invoker_ptr(new invoker<notice, i_implementation>(this, &i_implementation::notice_handler));
      _map[1] = i_invoker_ptr(new invoker<login, i_implementation>(this, &i_implementation::login_handler));
      _map[2] = i_invoker_ptr(new invoker<quit, i_implementation>(this, &i_implementation::quit_handler));
      _map[3] = i_invoker_ptr(new invoker<ping, i_implementation>(this, &i_implementation::ping_handler));
      _map[4] = i_invoker_ptr(new invoker<pong, i_implementation>(this, &i_implementation::pong_handler));
   }
   
   virtual void dispatch(constants::header_buffer& h, constants::body_buffer& b) {
      int id = header_coders::decode_header(h).id;
      if ( _map.find(id) == _map.end() ) {
         throw std::runtime_error("for type::id(" + boost::lexical_cast<std::string>(id) + ") handler not registered!");
      }
      _map[id]->dispatch(h, b);
   }

private:
   std::map<int, i_invoker_ptr> _map;
};

/***************************************************************************/


реализация:
Код


#include "header.hpp"

/***************************************************************************/

struct implementation: i_implementation {
   void notice_handler(notice&) {
      std::cout << "called: " << __PRETTY_FUNCTION__ << std::endl;
   }
   
   void login_handler(login&) {
      std::cout << "called: " << __PRETTY_FUNCTION__ << std::endl;
   }
   
   void quit_handler(quit&) {
      std::cout << "called: " << __PRETTY_FUNCTION__ << std::endl;
   }
   
   void ping_handler(ping&) {
      std::cout << "called: " << __PRETTY_FUNCTION__ << std::endl;
   }
   
   void pong_handler(pong&) {
      std::cout << "called: " << __PRETTY_FUNCTION__ << std::endl;
   }
};

/***************************************************************************/

struct server {
   server(boost::asio::io_service& ios, boost::uint16_t port)
      :_acceptor(ios, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
      _socket(ios),
      _idx(0),
      _impl(new implementation())
   {
      _acceptor.async_accept(_socket,
         boost::bind(&server::handle_accept, this, boost::asio::placeholders::error)
      );
   }

   ~server() {
      std::cout << "destructing server" << std::endl;
   }

   void handle_accept(const boost::system::error_code& e) {
      if ( !e ) {
         std::cout
            << "server: incoming connection from: "
            << _socket.remote_endpoint().address().to_string() << std::endl;
         start_read();
      } else {
         std::cout << "server: error on server accept" << std::endl;
      }
   }

   void start_read() {
      boost::asio::async_read(_socket,
         boost::asio::buffer(_header, _header.size()),
         boost::bind(&server::header_read_handler, this, boost::asio::placeholders::error)
      );
   }

   void header_read_handler(const boost::system::error_code& e) {
      if ( !e ) {
         std::cout << header_coders::decode_header(_header) << std::endl;
         boost::asio::async_read(_socket,
            boost::asio::buffer(_body, header_coders::decode_header(_header).body_length),
            boost::bind(&server::body_read_handler, this, boost::asio::placeholders::error)
         );
      } else {
         std::cout << "server: header read error" << std::endl;
      } 
   }

   void body_read_handler(const boost::system::error_code& e) {
      if ( !e ) {
         _impl->dispatch(_header, _body);
      } else {
      }
      start_read();
   }

   void start_write() {
      boost::asio::async_write(_socket,
         boost::asio::buffer(_header, _header.size()),
         boost::bind(&server::header_write_handler, this, boost::asio::placeholders::error)
      );
   }

   void header_write_handler(const boost::system::error_code& e) {
      if ( !e ) {
         boost::asio::async_read(_socket,
            boost::asio::buffer(_body, header_coders::decode_header(_header).body_length),
            boost::bind(&server::body_write_handler, this, boost::asio::placeholders::error)
         );
      } else {
         std::cout << "server: header write error" << std::endl;
      }
      start_write();
   }

   void body_write_handler(const boost::system::error_code& e) {
      if ( !e ) {
      } else {
      }
   }
   
   constants::header_buffer _header;
   constants::body_buffer _body;

private:
   boost::asio::ip::tcp::acceptor _acceptor;
   boost::asio::ip::tcp::socket _socket;
   int _idx;
   std::shared_ptr<implementation> _impl;
};

/***************************************************************************/

int main() {
   try {
      boost::asio::io_service ios;
      server server(ios, port);
      ios.run();
   } catch (const std::exception& e) {
      std::cout << "(exception): " << e.what() << std::endl;
   }
}

/***************************************************************************/


Автор: mes 28.10.2010, 08:36
Цитата(boostcoder @  27.10.2010,  23:21 Найти цитируемый пост)
1. клиент, при посылке команды, каким образом будет знать, на какую команду ждать ответа, а на какую нет?
2. серверу, чтоб послать ответ на какую-то команду, так же как и клиенту нужно знать, на какую команду отвечать, а на какую нет.

опять )) ничего лишнего ! ) только то что было в задании )
ну ладно .. 
сервер не посылает ответ, как таковой, он просто принимает команды и работает и побрабатывает их..
если ему когда нибудь (в зависмости от логики обработки, и независимо от приема команды клиента) захочеться послать клиенту команду, он просто ее посылает.. 
smile

сейчас код гляну

Добавлено через 14 минут и 47 секунд
посмотрел.. как и предполагалось по вступительным вопросам к коду.. опять вставили свою лепту.. smile
ну в принципе ничего так, только надо  вытащить нужное, и избавиться от лишнего.. 

Автор: mes 28.10.2010, 08:59
Цитата(boostcoder @  27.10.2010,  23:21 Найти цитируемый пост)
struct i_implementation {

по коду я понял, что у вас предполагается одинаковая база как для сервера, так и для клиента?
это жесткая привязка, которая может заставить пользователя писать костыль.. 

Цитата(boostcoder @  27.10.2010,  23:21 Найти цитируемый пост)
struct server {

Цитата(boostcoder @  27.10.2010,  23:21 Найти цитируемый пост)
  std::shared_ptr<implementation> _impl;

это у Вас моноблок получается.. нехорошо.. 

Цитата(boostcoder @  27.10.2010,  23:21 Найти цитируемый пост)
struct i_invoker {

к инвокерам претензий в принципе нет

Автор: mes 28.10.2010, 09:17
пока оставим сервер в покое.. принимает - молодец, с него пока хватит.. 
(только к клиенту этот подход не применять, и не отходить от нашего нижепредложенного плана )
рассмотрим net_client... 
нам важны сейчас две его функции , отправки и приема сырых данных.. 
сырые данные (исходя из кода) у нас выражены как (header&, body&)
на клиентена надо добавить два объекта.. rpc_sender и rpc_receiver
выглядят так
Код

struct rpc_sender {
  
   template<typename T>
   void send (const T&) {  ... raw_send (.. , ..);  }
   std::function<void(header&, body&)> raw_send;
};

Код

struct rpc_receiver {
  
   void dispatch (header&, body&) {
         std::cout << ..
   }
};

привязка к net_clientу посредством bind. 
для проверки можно связать сендер и ресивер непосредственно .. 

и еще раз ничего лишнего ! smile
фактически только реализовать сендер и привязку.. 

Автор: boostcoder 28.10.2010, 09:51
Цитата(mes @  28.10.2010,  08:36 Найти цитируемый пост)
опять )) ничего лишнего ! ) только то что было в задании )

это правда. я забежал вперед. просто в момент написания, мне казалось что идею я понял целиком..но когда дошло дело до отправки ответа, я понял, что ничего не понял..

Цитата(mes @  28.10.2010,  08:36 Найти цитируемый пост)
сервер не посылает ответ, как таковой, он просто принимает команды и работает и побрабатывает их..
если ему когда нибудь (в зависмости от логики обработки, и независимо от приема команды клиента) захочеться послать клиенту команду, он просто ее посылает.. 

т.е. команды для клиента и сервера разные? не связанные между собой?

Цитата(mes @  28.10.2010,  08:36 Найти цитируемый пост)
надо  вытащить нужное, и избавиться от лишнего.. 

от чего именно избавится?

Цитата(mes @  28.10.2010,  08:59 Найти цитируемый пост)
по коду я понял, что у вас предполагается одинаковая база как для сервера, так и для клиента?
это жесткая привязка, которая может заставить пользователя писать костыль.. 

это именно тот момент, из-за которого я недописал реализацию.

Цитата(mes @  28.10.2010,  08:59 Найти цитируемый пост)
это у Вас моноблок получается.. нехорошо.. 

ага.

не могли бы вы изменить код приведенный выше, так, чтоб в нем небыло ничего лишнего? т.к. я не очень понимаю что именно я должен сделать..

Добавлено через 1 минуту и 20 секунд
по последнему: вроде понял..

Добавлено через 3 минуты и 2 секунды
Цитата(boostcoder @  28.10.2010,  09:51 Найти цитируемый пост)
код приведенный выше, так, чтоб в нем небыло ничего лишнего?

т.е. мой код.

Автор: mes 28.10.2010, 10:56
Цитата(boostcoder @  28.10.2010,  08:51 Найти цитируемый пост)
не могли бы вы изменить код приведенный выше, так, чтоб в нем небыло ничего лишнего? т.к. я не очень понимаю что именно я должен сделать..


Цитата(mes @  28.10.2010,  08:17 Найти цитируемый пост)
пока оставим сервер в покое.. принимает - молодец, с него пока хватит.. 

Цитата(mes @  28.10.2010,  08:17 Найти цитируемый пост)
фактически только реализовать сендер и привязку.. 

 smile 

Автор: boostcoder 28.10.2010, 10:58
ок.

Добавлено через 3 минуты и 48 секунд
Цитата(mes @  28.10.2010,  09:17 Найти цитируемый пост)
нам важны сейчас две его функции , отправки и приема сырых данных.. 

по приему данных все ок. в коде сервера, в строке 77, заголовок и тело прочитаны. можно диспетчеризировать.

отправку пока оставим в покое я так понял.

сейчас клиента доделаю.

Автор: mes 28.10.2010, 11:13
Цитата(boostcoder @  28.10.2010,  08:51 Найти цитируемый пост)
т.е. команды для клиента и сервера разные? не связанные между собой?

при простой модели ( точка <-> точка )  команды симметричны  т.е. что у одного для отправки у другого для приема..

Добавлено через 4 минуты и 10 секунд
Цитата(boostcoder @  28.10.2010,  09:58 Найти цитируемый пост)
Цитата(mes @  28.10.2010,  09:17 )
нам важны сейчас две его функции , отправки и приема сырых данных.. 

по приему данных все ок. в коде сервера, в строке 77, заголовок и тело прочитаны. можно диспетчеризировать.

имелось ввиду что не важен сам процесс отправки, и другие функции такие как подключение..

наш rpc_sender с ресивером могут работать в том числе и без сетевого клиента ( если взаимодействие не через сеть)..
но это опять забегание вперед, для того чтоб немножко снизить туманость на пути к цели smile

Добавлено через 14 минут и 42 секунды
Цитата(boostcoder @  28.10.2010,  09:58 Найти цитируемый пост)
 строке 77, заголовок и тело прочитаны. можно диспетчеризировать.

да, только, чтоб можно было диспатчеризовать кому угодно, и развязать нам руки, вместо  _impl поставить  делегата 
smile

Автор: boostcoder 28.10.2010, 11:59
код клиента.
только не связал rpc_sender и rpc_receiver, ибо не уверен что все понял..
Код


#include "header.hpp"

/***************************************************************************/

struct rpc_sender {

   std::function<void()> raw_send;
   
   rpc_sender(constants::header_buffer& h, constants::body_buffer& b)
      :_header(h),_body(b)
   {}
   
   template<typename T>
   void send(const T& o) {
      serializer(_header, _body, o);
      raw_send();
   }

private:
   constants::header_buffer& _header;
   constants::body_buffer& _body;
};

/***************************************************************************/

struct rpc_receiver {
   rpc_receiver(constants::header_buffer& h, constants::body_buffer& b)
      :_header(h),_body(b)
   {}

   void dispatch() {
      std::cout << _header.begin() << std::endl;
   }

private:
   constants::header_buffer& _header;
   constants::body_buffer& _body;
};

/***************************************************************************/

struct client {
   client(boost::asio::io_service& ios, const char* ip, boost::uint16_t port)
      :_socket(ios),
      _idx(0)
   {
      boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(ip), port);
      _socket.connect(endpoint);
      std::cout << "client: connected to server" << std::endl;
      /**  */
      start_read();
   }

   ~client() {
      _socket.close();
      std::cout << "destructing client" << std::endl;
   }

   void start_read() {
      boost::asio::async_read(_socket,
         boost::asio::buffer(_header, _header.size()),
         boost::bind(&client::header_read_handler, this, boost::asio::placeholders::error)
      );
   }

   void raw_send() {
      boost::asio::async_write(_socket,
         boost::asio::buffer(_header, _header.size()),
         boost::bind(&client::header_write_handler, this, boost::asio::placeholders::error)
      );
   }

   void header_read_handler(const boost::system::error_code& e) {
      if ( !e ) {
         /** заголовок прочитан */
         boost::asio::async_read(_socket,
            boost::asio::buffer(_body, header_coders::decode_header(_header).body_length),
            boost::bind(&client::body_read_handler, this, boost::asio::placeholders::error)
         );
      } else {
         std::cout << "client: error read header" << std::endl;
      }
   }

   void body_read_handler(const boost::system::error_code& e) {
      if ( !e ) {
         /** тело прочитано */
         start_read();
      } else {
      }
   }

   void header_write_handler(const boost::system::error_code& e) {
      if ( !e ) {
         /** заголовок отправлен */
         boost::asio::async_write(_socket,
            boost::asio::buffer(_body, header_coders::decode_header(_header).body_length),
            boost::bind(&client::body_write_handler, this, boost::asio::placeholders::error)
         );
      } else {
         std::cout << "client: error on writing to server" << std::endl;
      }
   }

   void body_write_handler(const boost::system::error_code& e) {
      if ( !e ) {
         /** тело отправлено */
      } else {
         std::cout << "client: error on writing to server" << std::endl;
      }
   }

   constants::header_buffer _header;
   constants::body_buffer _body;

private:
   boost::asio::ip::tcp::socket _socket;
   int _idx;
};

/***************************************************************************/

int main() {
   try {
      boost::asio::io_service ios;
      client client(ios, host, port);
      ios.run();
   } catch (const std::exception& e) {
      std::cout << "(exception): " << e.what() << std::endl;
   }
}

/***************************************************************************/



Автор: mes 28.10.2010, 12:02
Цитата(boostcoder @  28.10.2010,  10:59 Найти цитируемый пост)
  std::function<void(header&, body&)> raw_send;


Добавлено через 1 минуту и 29 секунд
Цитата(boostcoder @  28.10.2010,  10:59 Найти цитируемый пост)
rpc_receiver {
   rpc_receiver(constants::header_buffer& h, constants::body_buffer& b)
      :_header(h),_body(b)

нет.. 

Код

struct rpc_receiver {
   
   void dispatch(header&, body&) {
      std::cout << _header.begin() << std::endl;
   }



Добавлено через 3 минуты и 11 секунд
выкидываем пока клиента... а то Вас так прям и тянет в сторону ...

Добавлено через 4 минуты и 52 секунды
пишем только сендер и ресивер.. связываем выход сендера, со входом ресивера.. 
при отправке пользователем тпизированного сообщения, ресивер должен распечать его в сыром виде.. 
smile

Добавлено через 5 минут и 54 секунды
т.е связаны должны быть  raw_send и dispatch 
smile

Автор: boostcoder 28.10.2010, 12:09
вот.
Код


#include "header.hpp"

/***************************************************************************/

struct rpc_sender {

   std::function<void(constants::header_buffer&, constants::body_buffer&)> raw_send;
   
   rpc_sender() {}
   
   template<typename T>
   void send(const T& o) {
      raw_send(...); /** объекты заголовка и тела должны быть локальными? */
   }
};

/***************************************************************************/

struct rpc_receiver {
   rpc_receiver() {}

   void dispatch(constants::header_buffer& h, constants::body_buffer& и) {
      std::cout << р.begin() << std::endl;
   }
};

/***************************************************************************/

struct client {
   client(boost::asio::io_service& ios, const char* ip, boost::uint16_t port)
      :_socket(ios),
      _idx(0)
   {
      boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(ip), port);
      _socket.connect(endpoint);
      std::cout << "client: connected to server" << std::endl;
      /**  */
      start_read();
   }

   ~client() {
      _socket.close();
      std::cout << "destructing client" << std::endl;
   }

   void start_read() {
      boost::asio::async_read(_socket,
         boost::asio::buffer(_header, _header.size()),
         boost::bind(&client::header_read_handler, this, boost::asio::placeholders::error)
      );
   }

   void raw_send() {
      boost::asio::async_write(_socket,
         boost::asio::buffer(_header, _header.size()),
         boost::bind(&client::header_write_handler, this, boost::asio::placeholders::error)
      );
   }

   void header_read_handler(const boost::system::error_code& e) {
      if ( !e ) {
         /** заголовок прочитан */
         boost::asio::async_read(_socket,
            boost::asio::buffer(_body, header_coders::decode_header(_header).body_length),
            boost::bind(&client::body_read_handler, this, boost::asio::placeholders::error)
         );
      } else {
         std::cout << "client: error read header" << std::endl;
      }
   }

   void body_read_handler(const boost::system::error_code& e) {
      if ( !e ) {
         /** тело прочитано */
         start_read();
      } else {
      }
   }

   void header_write_handler(const boost::system::error_code& e) {
      if ( !e ) {
         /** заголовок отправлен */
         boost::asio::async_write(_socket,
            boost::asio::buffer(_body, header_coders::decode_header(_header).body_length),
            boost::bind(&client::body_write_handler, this, boost::asio::placeholders::error)
         );
      } else {
         std::cout << "client: error on writing to server" << std::endl;
      }
   }

   void body_write_handler(const boost::system::error_code& e) {
      if ( !e ) {
         /** тело отправлено */
      } else {
         std::cout << "client: error on writing to server" << std::endl;
      }
   }

   constants::header_buffer _header;
   constants::body_buffer _body;

private:
   boost::asio::ip::tcp::socket _socket;
   int _idx;
};

/***************************************************************************/

int main() {
   try {
      boost::asio::io_service ios;
      client client(ios, host, port);
      ios.run();
   } catch (const std::exception& e) {
      std::cout << "(exception): " << e.what() << std::endl;
   }
}

/***************************************************************************/


Автор: mes 28.10.2010, 12:14
Цитата(boostcoder @  28.10.2010,  11:09 Найти цитируемый пост)
/** объекты заголовка и тела должны быть локальными? */

сейчас да.. после , если захотите избежать лишних копирований, или иметь возможность помещать в очередь ожидания, они пакуются в полиморфный объект (raw_packet) но нам это сейчас лишнее..

Добавлено через 1 минуту и 45 секунд
а посмотрел на клиент.. вы не то связали..  raw_send  не привязывается к клиенту ..

Добавлено через 2 минуты и 17 секунд
т.е. не привязывается как хэндлер записи... 

Автор: boostcoder 28.10.2010, 12:16
Цитата(mes @  28.10.2010,  12:02 Найти цитируемый пост)
выкидываем пока клиента... а то Вас так прям и тянет в сторону ...

Добавлено через 4 минуты и 52 секунды
пишем только сендер и ресивер.. связываем выход сендера, со входом ресивера.. 
при отправке пользователем тпизированного сообщения, ресивер должен распечать его в сыром виде.. 

т.е. код клиента выкидываем, и в main() связываем только два объекта sender и receiver ?

Автор: mes 28.10.2010, 12:20
Цитата(boostcoder @  28.10.2010,  11:16 Найти цитируемый пост)
т.е. код клиента выкидываем, и в main() связываем только два объекта sender и receiver ? 

сейчас да.. чтоб Вы поняли как клиент подключать smile

Автор: boostcoder 28.10.2010, 12:33
так?
Код

/***************************************************************************/

struct rpc_sender {

   std::function<void(constants::header_buffer&, constants::body_buffer&)> raw_send;

   rpc_sender() {}

   template<typename T>
   void send(const T& o) {
      raw_send(_header, _body);
   }
private:
   constants::header_buffer _header;
   constants::body_buffer _body;
};

/***************************************************************************/

struct rpc_receiver {
   rpc_receiver() {}

   void dispatch(constants::header_buffer& h, constants::body_buffer& b) {
      std::cout << h.begin() << std::endl;
   }
};

/***************************************************************************/

int main() {
   rpc_sender sender;
   rpc_receiver receiver;

   sender.raw_send = boost::bind(&rpc_receiver::dispatch, &receiver);
}

/***************************************************************************/


Автор: mes 28.10.2010, 12:38
вобще то так :
Код

struct rpc_sender {
   std::function<void(constants::header_buffer&, constants::body_buffer&)> raw_send;
   rpc_sender() {}

   template<typename T>
     void send(const T& o) {

        constants::header_buffer _header;
        constants::body_buffer    _body;
       
        serializer(_header, _body, o);      
        raw_send(_header, _body);
    }
};


Автор: boostcoder 28.10.2010, 12:45
хорошо.
что дальше?

Автор: mes 28.10.2010, 12:47
теперь к ресиверу нужно добавить таблицу диспетчеров.. 
где dispacther полиморфно принимает сырой пакет, конвертирует  его в нужный тип, и отдает делегату .. 

Автор: boostcoder 28.10.2010, 13:02
так?
Код

/***************************************************************************/

struct i_invoker {
   virtual void dispatch(constants::header_buffer&, constants::body_buffer&) = 0;
};
typedef std::shared_ptr<i_invoker> i_invoker_ptr;

template<typename T, typename C>
struct invoker: i_invoker {
   typedef void(C::*handler_t)(T&);

   invoker(C* p, handler_t h):_this(p),_handler(h) {}

   virtual void dispatch(constants::header_buffer& h, constants::body_buffer& b) {
      T data;
      deserializer(data, h, b);
      (*_this.*_handler)(data);
   }

private:
   C* _this;
   handler_t _handler;
};

/***************************************************************************/

struct rpc_sender {
   std::function<void(constants::header_buffer&, constants::body_buffer&)> raw_send;
   rpc_sender() {}

   template<typename T>
   void send(const T& o) {
      constants::header_buffer _header;
      constants::body_buffer _body;

      serializer(_header, _body, o);
      raw_send(_header, _body);
   }
};

/***************************************************************************/

struct rpc_receiver {
   rpc_receiver() {
      /** init */
   }

   virtual void dispatch(constants::header_buffer& h, constants::body_buffer& b) {
      int id = header_coders::decode_header(h).id;
      _map[id]->dispatch(h, b);
   }

private:
   std::map<int, i_invoker_ptr> _map;
};

/***************************************************************************/

int main() {
   rpc_sender sender;
   rpc_receiver receiver;

   sender.raw_send = boost::bind(&rpc_receiver::dispatch, &receiver);
}

/***************************************************************************/


Автор: mes 28.10.2010, 13:10
опять не совсем.. слишком рано для предположения о передачи типизированного сообщения ресиверу... ресивер это чать rpc, a сообщения должны обрабатываться в пользовательской части ..
smile

Добавлено через 2 минуты и 45 секунд
Цитата(boostcoder @  28.10.2010,  12:02 Найти цитируемый пост)
struct rpc_receiver {
   rpc_receiver() {
      /** init */
   }
   virtual void dispatch(constants::header_buffer& h, constants::body_buffer& b) {
      int id = header_coders::decode_header(h).id;
      _map[id]->dispatch(h, b);
   }

Код

struct rpc_receiver {
   rpc_receiver() {
      /** не нужен : init */
   }
    /* стереть : virtual */ 
      void dispatch(constants::header_buffer& h, constants::body_buffer& b) {
      int id = header_coders::decode_header(h).id;
      _map[id]->dispatch(h, b);
   }


Автор: boostcoder 28.10.2010, 13:16
значит так:
Код

/***************************************************************************/

struct i_invoker {
   virtual void dispatch(constants::header_buffer&, constants::body_buffer&) = 0;
};
typedef std::shared_ptr<i_invoker> i_invoker_ptr;

template<typename T, typename C>
struct invoker: i_invoker {
   typedef void(C::*handler_t)(T&);

   invoker(C* p, handler_t h):_this(p),_handler(h) {}

   virtual void dispatch(constants::header_buffer& h, constants::body_buffer& b) {
      T data;
      deserializer(data, h, b);
      (*_this.*_handler)(data);
   }

private:
   C* _this;
   handler_t _handler;
};

/***************************************************************************/

struct rpc_sender {
   std::function<void(constants::header_buffer&, constants::body_buffer&)> raw_send;
   rpc_sender() {}

   template<typename T>
   void send(const T& o) {
      constants::header_buffer _header;
      constants::body_buffer _body;

      serializer(_header, _body, o);
      raw_send(_header, _body);
   }
};

/***************************************************************************/

struct rpc_receiver {
   rpc_receiver() {}

   void dispatch(constants::header_buffer& h, constants::body_buffer& b) {
      int id = header_coders::decode_header(h).id;
      _map[id]->dispatch(h, b);
   }

private:
   std::map<int, i_invoker_ptr> _map;
};

/***************************************************************************/

int main() {
   rpc_sender sender;
   rpc_receiver receiver;

   sender.raw_send = boost::bind(&rpc_receiver::dispatch, &receiver);
}

/***************************************************************************/



Цитата(mes @  28.10.2010,  13:10 Найти цитируемый пост)
слишком рано для предположения о передачи типизированного сообщения ресиверу... ресивер это чать rpc, a сообщения должны обрабатываться в пользовательской части ..

не понимаю к чему это сказано....

Автор: mes 28.10.2010, 13:17
Цитата(boostcoder @  28.10.2010,  12:02 Найти цитируемый пост)

template<typename T, typename C>
struct invoker: i_invoker {
   typedef std::function< void()(T&)> func_t; // пока такой делегат

   virtual void dispatch(constants::header_buffer& h, constants::body_buffer& b) {
      T data;
      deserializer(data, h, b);
      m_func (data);
   }
...

};


Автор: boostcoder 28.10.2010, 13:18

я просто не понимаю к чему это должно привести..
может оказаться так, как уже было... что мы четыре страницы не могли понять друг-друга..

Автор: mes 28.10.2010, 13:22
Цитата(boostcoder @  28.10.2010,  12:18 Найти цитируемый пост)
может оказаться так, как уже было... что мы четыре страницы не могли понять друг-друга..

сейчас так и происходит..поэтому я частями и даю.. smile
ничего скоро уже должно подойти к цели 
smile

Добавлено через 1 минуту и 29 секунд
добавляем к ресиверу функцию (шаблонную) для регистрации обработчика (сейчас лямбда функции)

Автор: boostcoder 28.10.2010, 13:37
ага.
Код

/***************************************************************************/

struct i_invoker {
   virtual void dispatch(constants::header_buffer&, constants::body_buffer&) = 0;
};
typedef std::shared_ptr<i_invoker> i_invoker_ptr;

template<typename T>
struct invoker: i_invoker {
   typedef std::function<void(T&)> func_t;

   invoker(func_t& f):m_func(f){}

   virtual void dispatch(constants::header_buffer& h, constants::body_buffer& b) {
      T data;
      deserializer(data, h, b);
      m_func(data);
   }
   func_t m_func;
};

/***************************************************************************/

struct rpc_sender {
   std::function<void(constants::header_buffer&, constants::body_buffer&)> raw_send;
   rpc_sender() {}

   template<typename T>
   void send(const T& o) {
      constants::header_buffer _header;
      constants::body_buffer _body;

      serializer(_header, _body, o);
      raw_send(_header, _body);
   }
};

/***************************************************************************/

struct rpc_receiver {
   rpc_receiver() {}

   void dispatch(constants::header_buffer& h, constants::body_buffer& b) {
      int id = header_coders::decode_header(h).id;
      _map[id]->dispatch(h, b);
   }

   template<typename T>
   void reg(typename i_invoker<T>::func_t& f) {
      _map[T::id] = i_invoker_ptr(new invoker<T>(f));
   }

private:
   std::map<int, i_invoker_ptr> _map;
};

/***************************************************************************/

int main() {
   rpc_sender sender;
   rpc_receiver receiver;

   sender.raw_send = boost::bind(&rpc_receiver::dispatch, &receiver);
}

/***************************************************************************/



Добавлено @ 13:38
код не компилил. так что использовать как псевдокод.

Автор: mes 28.10.2010, 13:38
как сделаете заводите два сообщения query и result
и вот тестовый пример :
Код


int main() {
// ....................................................

   rpc_sender client_sender;
   rpc_receiver client_receiver;
  
    client_recevier.set_handler<result> ([](result const&)
    {
         std::cout << "result,  hurra! " << std::endl;
    })
// ....................................................

   rpc_sender  server_sender;
   rpc_receiver server_receiver;

    client_recevier.set_handler<query> ([&client_sender](query const&)
    {
         std::cout << "query.. "
         client_sender.send (result());
    })
// ....................................................

   client_sender.raw_send = boost::bind(&rpc_receiver::dispatch, &server_receiver);
   server_sender.raw_send = boost::bind(&rpc_receiver::dispatch, &client_receiver);

// ....................................................

    client_sender.send (query());

// ....................................................
}


set_handler это reg.

Автор: boostcoder 28.10.2010, 13:40
Цитата(boostcoder @  28.10.2010,  13:37 Найти цитируемый пост)
_map[T::id] = i_invoker_ptr(new invoker<T>(f));

тут непонятно что использовать как ключ.. или же эту часть должен сгенерировать кодогенератор?

Добавлено через 1 минуту и 59 секунд
Цитата(mes @  28.10.2010,  13:38 Найти цитируемый пост)
   client_sender.raw_send = boost::bind(&rpc_receiver::dispatch, &server_receiver);
   server_sender.raw_send = boost::bind(&rpc_receiver::dispatch, &client_receiver);

 smile  smile 

Автор: mes 28.10.2010, 13:42
Цитата(boostcoder @  28.10.2010,  12:40 Найти цитируемый пост)
тут непонятно что использовать как ключ.. или же эту часть должен сгенерировать кодогенератор? 

T::id .. не понял вопроса.. 
кодогенератора у нас нет пока (мы догаваривались же).. все вручную smile

Добавлено @ 13:43
Цитата(boostcoder @  28.10.2010,  12:40 Найти цитируемый пост)
    


Цитата(boostcoder @  28.10.2010,  12:40 Найти цитируемый пост)
   client_sender.raw_send = boost::bind(&rpc_receiver::dispatch, &server_receiver);
   server_sender.raw_send = boost::bind(&rpc_receiver::dispatch, &client_receiver);
..  smile 
     

что то не так ? 
 smile 

Автор: boostcoder 28.10.2010, 13:45
Цитата(mes @  28.10.2010,  13:42 Найти цитируемый пост)
что то не так ? 

все так.
еще одно непонимание разрешилось)

Автор: mes 28.10.2010, 13:45
Цитата(boostcoder @  28.10.2010,  12:45 Найти цитируемый пост)
все так.
еще одно непонимание разрешилось) 

отлично smile

Добавлено @ 13:47
как тестовый пример заработает, считайте что главное сделали.
smile

Добавлено через 10 минут и 36 секунд
как закончите выкладывайте этот примерчик на онлайн-компиляторе..
чтоб можно было визуально оценить куда дальше рельсы класть smile

Автор: mes 28.10.2010, 14:38
чего то долговато.. там вроде ж у Вас все готово.. или опять чего то лишнее прикручиваете ?
 smile 

Автор: boostcoder 28.10.2010, 14:45
smile 
вот: http://liveworkspace.org/code/a372e0ad39ad54853d988c17158dc121
что-то компилятор ругается..

Добавлено через 13 минут и 9 секунд
не вижу ошибки.. вполне законное связывание..

Автор: mes 28.10.2010, 15:03
Цитата(boostcoder @  28.10.2010,  13:45 Найти цитируемый пост)
не вижу ошибки.. вполне законное связывание.. 

это моя ошибка, в связывании забыты плейсхолдеры smile

Добавлено через 2 минуты и 19 секунд
терь не хватает кода сериализации, чтоб увидеть результат.. 
smile

Автор: boostcoder 28.10.2010, 15:07
ы)
http://liveworkspace.org/code/3c9e2679054ce612756b18c4669b1ff3

Добавлено через 18 секунд
осталось понять почему нет вывода..

Добавлено через 2 минуты и 4 секунды
Цитата(mes @  28.10.2010,  15:03 Найти цитируемый пост)
терь не хватает кода сериализации, чтоб увидеть результат..

это спешка.. сейчас сделаю..

Автор: boostcoder 28.10.2010, 15:40
вот: http://liveworkspace.org/code/e41ca2a8c07fc54bc49cabd172c9e0ab

но есть один вопрос не позволяющий завершить код: как быть с meta_dir ? это индикатор направления пакета.

Добавлено через 44 секунды
убрать пока его?

Автор: mes 28.10.2010, 15:57
Цитата(boostcoder @  28.10.2010,  14:40 Найти цитируемый пост)
но есть один вопрос не позволяющий завершить код: как быть с meta_dir ? это индикатор направления пакета.

ну а откуда он взялся ?  smile   в плане работ его не было smile  smile 
 smile

Добавлено через 19 секунд
сейчас код гляну smile

Автор: mes 28.10.2010, 16:33
чего то вы там  хидерами и бодями нахимичили.. 

Автор: boostcoder 28.10.2010, 16:36
Цитата(mes @  28.10.2010,  15:57 Найти цитируемый пост)
ну а откуда он взялся ?     в плане работ его не было    

значит не будет  smile 
еще с сериализацией разберусь...

Автор: mes 28.10.2010, 16:57
Цитата(boostcoder @  28.10.2010,  15:36 Найти цитируемый пост)
еще с сериализацией разберусь... 

я думаю сейчас пора убраться от лишних зависимостей реализации.. и объединить хидер с бодом в одном пакете.. 
smile соответсвенно все функции принимавшие эти два параметра , терь будут принимать один .. 

Автор: boostcoder 28.10.2010, 17:00
Цитата(mes @  28.10.2010,  16:57 Найти цитируемый пост)
хидер с бодом в одном пакете.. 

затайпдефить новый тип?

Добавлено через 7 минут и 48 секунд
вот: http://liveworkspace.org/code/ae95e786e65835543dbed5ee67d368e5
осталось понять почему не вызывается invoker::dispatch() ..

Добавлено через 12 минут и 2 секунды
карта почему-то пустая. хотя зарегано по одному обработчику..

Автор: boostcoder 28.10.2010, 17:20
с этим разобрался: http://liveworkspace.org/code/2da9190f091a4ccbf28913d37ff318ba
еще ответ на query не срабатывает smile

Добавлено через 2 минуты и 47 секунд
вот. и с этим разобрался: http://liveworkspace.org/code/43a5b1c35f66f6a6f9868a3941c5a04b
гляньте плиз, я ничего не напутал?
можно считать этот код опорной точкой для продолжения?

Добавлено через 9 минут и 55 секунд
и еще: http://liveworkspace.org/code/f936ac96af0b5f32780ef89ab01d68e0

Автор: mes 28.10.2010, 17:36
Цитата(boostcoder @  28.10.2010,  16:20 Найти цитируемый пост)
гляньте плиз, я ничего не напутал?

на вид пока все ок )

Цитата(boostcoder @  28.10.2010,  16:20 Найти цитируемый пост)
можно считать этот код опорной точкой для продолжения? 

не совсем.. нужно избавиться от лишних. зависимостей.. пока эту часть консервируем и делаем от нее ответление.. (вначале все таки упустили, терь придется поправлять)

заводим структуру 
Код

struct rpc_packet
{
       size_t id;
// пока в ней ничего большего.. 
};

убираем весь код сериализации в сторону, и вмето хидера_с_бодей ставим нашу структуру..


Автор: boostcoder 28.10.2010, 18:11
Цитата(mes @  28.10.2010,  17:36 Найти цитируемый пост)
нужно избавиться от лишних. зависимостей..

каких именно, и каким образом?

Цитата(mes @  28.10.2010,  17:36 Найти цитируемый пост)
пока эту часть консервируем и делаем от нее ответление..

т.е. на основе этого кода? или писать с нуля?

Цитата(mes @  28.10.2010,  17:36 Найти цитируемый пост)
убираем весь код сериализации в сторону, и вмето хидера_с_бодей ставим нашу структуру..

т.е. эту структуру вписать в другой дополнительный хидер?

Автор: mes 28.10.2010, 18:16
Цитата(boostcoder @  28.10.2010,  17:11 Найти цитируемый пост)
каких именно, и каким образом?

так же как от нет_клиента избавились smile


Цитата(boostcoder @  28.10.2010,  17:11 Найти цитируемый пост)
т.е. на основе этого кода? или писать с нуля?

зачем с нуля ? сейчас код весь нужный, только неправильно расфасован.. 
создаете новый файл, перекидываете туда все кроме сериализации и меняете параметры функций на rpc_packet.. 


Цитата(boostcoder @  28.10.2010,  17:11 Найти цитируемый пост)
т.е. эту структуру вписать в другой дополнительный хидер? 

нет, грубо говоря сериализацию вынести в другой хидер... 
smile

Добавлено через 33 секунды
сейчас подождите.. я сам сделаю .. 

Автор: boostcoder 28.10.2010, 18:24
Цитата(mes @  28.10.2010,  18:16 Найти цитируемый пост)
сейчас подождите.. я сам сделаю .. 

ы smile 

Автор: mes 28.10.2010, 18:27
вот что я просил :
http://liveworkspace.org/code/bae836150c287a809bf7a3628db165f3
 smile 

Автор: boostcoder 28.10.2010, 18:32
Цитата(mes @  28.10.2010,  18:27 Найти цитируемый пост)
вот что я просил

ага. ок.
что теперь?

Добавлено через 5 минут и 6 секунд
кстати, а зачем нам rpc_packet ?
или это новый тип взамен хедера и бади?

Добавлено через 6 минут и 29 секунд
по идее - да.
в нем будут локальные буфера и сериализация/десериализация. smile

Автор: mes 28.10.2010, 18:40
Цитата(boostcoder @  28.10.2010,  17:32 Найти цитируемый пост)
что теперь? 

терь надо определиться с пакетом.. какое взаимодействие он должен поддерживать.. 


Автор: boostcoder 28.10.2010, 18:44
так как в планах у нас есть динамическое создание обработчиков(а по мне, так обработчики должны быть методами класса), в нем должен быть идентификатор класса обработчика. так же, можно реализовать передачу служебных сообщений таких как например, исключения smile 

Автор: mes 28.10.2010, 18:46
минутку не торопитесь... давайте приведем вначале к стартовой точке.. 
smile

Автор: boostcoder 28.10.2010, 18:47
Цитата(boostcoder @  28.10.2010,  18:44 Найти цитируемый пост)
в нем должен быть идентификатор класса обработчика

т.е. после десериализации пакета, определяем ID класса в котором находится обработчик для команды этого типа. просматриваем карту на предмет наличия этого класса. если есть - берем диспетчера этого класса и передаем ему пакет. если нет - бросить исключение клиенту обратно.

Добавлено через 33 секунды
Цитата(mes @  28.10.2010,  18:46 Найти цитируемый пост)
минутку не торопитесь

мысли вслух...

Автор: mes 28.10.2010, 18:48
подумав все таки решил, что rpc_packet это бинарный набор данных... 
т.е терь возвращаем сериализацию на родину.. только терь она должна внутрь пакета сериализовать..

Добавлено через 28 секунд
вы пока с сериализацией мучайтесь, а я пока про ресивер подумаю ))

Автор: mes 28.10.2010, 19:06
и еще invoker нужно  занестить в ресивер.. 
и разгрузить вызов функции инвокера через внешнюю шаблонную функцию.. 


Автор: boostcoder 28.10.2010, 19:16
Цитата(mes @  28.10.2010,  19:06 Найти цитируемый пост)
и еще invoker нужно  занестить в ресивер.. 
и разгрузить вызов функции инвокера через внешнюю шаблонную функцию.. 

эм.. поясните ;)

Автор: mes 28.10.2010, 19:37
Цитата(boostcoder @  28.10.2010,  18:16 Найти цитируемый пост)
 занестить

в принципе пока не обязательно.. имелось ввиду определить его  в пространстве ресивера..
Код

struct receiver { struct invoker; };

Автор: boostcoder 28.10.2010, 19:39
Цитата(mes @  28.10.2010,  19:37 Найти цитируемый пост)
в принципе пока не обязательно.. имелось ввиду определить его  в пространстве ресивера..

ок. делаю.

Автор: mes 28.10.2010, 19:43
Цитата(boostcoder @  28.10.2010,  18:16 Найти цитируемый пост)
и разгрузить вызов функции инвокера через внешнюю шаблонную функцию.

сделать две шаблонные функции
Код

packet to_paket(T const& );
T  from_packet (packet const&);

внести в них всю работу с сериализацией.. 
а в сендере, ресивере и, соответственно, инвокере передачу пакета осуществлять по константной ссылке..

Код

struct sender { .. void send (const T& msg) { raw_send (to_packet<T>(msg)); };
struct invoker { ..void dispatch (..) { func( from_packet<T>(pack)); };


Добавлено через 1 минуту и 50 секунд
Цитата(mes @  28.10.2010,  18:43 Найти цитируемый пост)
to_paket

названия не очень удачные.. но если ничего лучше не придет в голову, можно пока так оставить smile

Автор: boostcoder 28.10.2010, 19:53
ок. и это сделаю.

Добавлено через 12 минут и 55 секунд
это:
Цитата(mes @  28.10.2010,  19:37 Найти цитируемый пост)
struct receiver { struct invoker; };

и это:
Цитата(mes @  28.10.2010,  19:43 Найти цитируемый пост)
struct sender { .. void send (const T& msg) { raw_send (to_packet<T>(msg)); };
struct invoker { ..void dispatch (..) { func( from_packet<T>(pack)); };

так как делать?

Автор: mes 28.10.2010, 20:14
Цитата(boostcoder @  28.10.2010,  18:53 Найти цитируемый пост)
так как делать? 

вобще то одно другому не мешает..

покажите что у Вас сейчас 

Автор: boostcoder 28.10.2010, 20:23
пока это: http://liveworkspace.org/code/65e326b96f505a6c875cf88e243f2c9b

Автор: mes 28.10.2010, 20:43
Цитата(boostcoder @  28.10.2010,  19:23 Найти цитируемый пост)
пока это: http://liveworkspace.org/code/65e326b96f50...75cf88e243f2c9b 

я думал вы хоть пакет уже добавили..  smile

Добавлено через 1 минуту и 27 секунд
кстати имхо подход не очень верный.. Вы пытаетесь к сериализации добавить пакет... а нужно с другой строны подходить, к пакету добавлять сериализацию smile
так будет легче и яснее )

Автор: boostcoder 28.10.2010, 20:44
так я не был уверен что все правильно понял. выше написал.
сейчас все будет.

Автор: mes 28.10.2010, 20:52
вот вам протипы пакета и пакетных функций..
Код

struct rpc_packet {
  size_t id;
};

template<class T>
rpc_packet rpc_pack (T const&)
{
      rpc_packet pack; 
      pack.id = T::meta_id;
      return pack;
}
template<class T>
T rpc_unpack (rpc_packet const&)
{
   return T();
}

вся сериализация должна быть в них ..

Добавлено @ 20:55
тогда сендер будет выглядить так :
Код

struct rpc_sender {
   std::function<void(rpc_packet const&)> raw_send;
   rpc_sender() {}

   template<typename T>
   void send(const T& o) {
        raw_send(rpc_pack<T>(o));
   }
};

и является теперь не зависимым от устройства рпц_библиотеки 

с остальным будет также.. 
smile

это я Вам опять немножко из будущего вытащил 
smile

Автор: boostcoder 28.10.2010, 20:58
минутку...ща все будет..

на LWS сделал выше область редактора кода, и размер шрифта помельче. вроде как повместительней должно быть.


Добавлено через 14 минут и 31 секунду
Цитата(mes @  28.10.2010,  20:52 Найти цитируемый пост)
struct rpc_packet {
  size_t id;
};

в ней же должны быть и хедер и бади. так?

Автор: mes 28.10.2010, 21:16
Цитата(boostcoder @  28.10.2010,  19:58 Найти цитируемый пост)
в ней же должны быть и хедер и бади. так? 

да, если нужны.. и как нужны )

Добавлено через 2 минуты и 18 секунд
вот я подправил остальное : http://liveworkspace.org/code/baa9b08e00519c82392bdb22c903daba

Автор: mes 28.10.2010, 21:31
после можно будет  написать класс rpc_receiver2, с инвокерами которые будут вызывать не function, а метод класса..
объект класса будет хранить ресивер..
P.S.старый ресивер не выкидывайте.. 





Автор: boostcoder 28.10.2010, 22:01
эврика!
http://liveworkspace.org/code/661062d84fc5980f8cbe28b7e5403890

сейчас еще пересмотрю код. кодировщик/декодировщик заголовка пакета нужно куда-то всунуть.

Добавлено @ 22:02
гляньте пожалуйста, может еще что-то найдете ;)

Добавлено через 9 минут и 28 секунд
навел порядок: http://liveworkspace.org/code/e1be046e609bab24d44396051e4c81b6

Добавлено через 12 минут и 32 секунды
теперь, как я предполагал, rpc_packet::id это идентификатор класса с его обработчиком?

Автор: mes 28.10.2010, 22:41
Цитата(boostcoder @  28.10.2010,  21:01 Найти цитируемый пост)
гляньте пожалуйста, может еще что-то найдете ;)

Код

struct rpc_packet {
   size_t id;
   constants::header_buffer header;


а для чего пакету хидер как буффер ?!  
сделайте лучше возможность сериализации пакета..

Добавлено через 3 минуты и 14 секунд
а paket::body нам тоже не нужен статического размера.. и можно заменить на строку/вектор..

Добавлено через 12 минут и 35 секунд
Код

   void dispatch(const types::rpc_packet& pack) {
...
      int id = types::decode_header(pack.header).id;
      _map[id]->dispatch(pack);
   }


Код

  void dispatch(const types::rpc_packet& pack) {      
        
      _map[id]->dispatch(pack.header.id); // либо pack.id
   //  и вместо _map[] нужно _map.find()..
   }

Автор: mes 28.10.2010, 22:56
как это подправите, можно считать полученное отправной точкой.. 

Автор: boostcoder 28.10.2010, 22:58
ок. сейчас..

Автор: mes 28.10.2010, 22:58
Цитата(boostcoder @  28.10.2010,  21:01 Найти цитируемый пост)
сейчас еще пересмотрю код. кодировщик/декодировщик заголовка пакета нужно куда-то всунуть

это уровень сети... здесь нам не нужно..

Добавлено @ 23:00
вобщем  нужно примерно так :
Код

struct packet {
    size_t id;
    std::string raw_data;   // std::vector 
};


все остальное (из constants) в мусорку 
smile


Автор: mes 28.10.2010, 23:37
не дождался , сам подправил :
http://liveworkspace.org/code/aa539e62ca1c9439d0017fe1a180288b
smile

Добавлено @ 23:46
пусть будет здесь, для надежности.. 

Код

#include <iostream>
#include <functional>
#include <array>
#include <memory>
#include <map>
#include <sstream>

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>


/***************************************************************************/
   struct rpc_packet {
      size_t id;
      std::string raw_data;  
   };

   template<typename T>
   rpc_packet rpc_pack(T const& o) {
      rpc_packet pack;
      pack.id = T::meta_id;
      std::ostringstream os;
      boost::archive::text_oarchive(os) << o;
      pack.raw_data = os.str();
   
      return pack;
   }
   template<typename T>
   T rpc_unpack(rpc_packet const& pack) {
      T data;
      std::istringstream is( pack.raw_data );
      boost::archive::text_iarchive(is) >> data;
      return data;
   }

/***************************************************************************/

struct rpc_sender {
   std::function<void( rpc_packet const&)> raw_send;
   rpc_sender() {}

   template<typename T>
   void send(const T& o) {
      raw_send(rpc_pack<T>(o));
   }
};

/***************************************************************************/

struct rpc_receiver {
   rpc_receiver() {}
   
   struct i_invoker {
      virtual void dispatch(rpc_packet const &) = 0;
   };
   typedef std::shared_ptr<i_invoker> i_invoker_ptr;

   template<typename T>
   struct invoker;
   
   void dispatch(rpc_packet const& pack) {
       
      auto it  = _map.find(pack.id);
      if ( it != _map.end() && it->second )
         it->second->dispatch(pack);
   }

   template<typename T>
   void set_handler(typename invoker<T>::func_t f) {
      _map[T::meta_id] = i_invoker_ptr(new invoker<T>(f));
   }

private:
   std::map<int, i_invoker_ptr> _map;
};

template<typename T>
struct rpc_receiver::invoker: i_invoker {
   typedef std::function<void(const T&)> func_t;

   invoker(func_t& f):m_func(f){}

   virtual void dispatch(rpc_packet const& pack) {

      m_func(rpc_unpack<T>(pack));
   }
   
private:
   func_t m_func;
};

/***************************************************************************/

struct query {
   query() {}
   query(const std::string& s, int c):message(s),code(c) {}

   enum { meta_id = 0 };
   std::string message;
   int code;

private:
   friend class boost::serialization::access;
   template<typename archive_type>
   void serialize(archive_type& ar, const unsigned int) {
      ar & message
         & code;
   }
};

struct result {
   result() {}
   result(const std::string& s, int c):message(s),code(c) {}
   
   enum { meta_id = 1 };
   std::string message;
   int code;

private:
   friend class boost::serialization::access;
   template<typename archive_type>
   void serialize(archive_type& ar, const unsigned int) {
      ar & message
         & code;
   }
};

/***************************************************************************/

int main() {
   /** client */
   rpc_sender client_sender;
   rpc_receiver client_receiver;
   
   client_receiver.set_handler<result>(
      [](const result& r) {
         std::cout
         << "from server:" << std::endl
         << "   msg : " << r.message << std::endl
         << "   code: " << r.code << std::endl;
      }
   );
   
   /** server */
   rpc_sender server_sender;
   rpc_receiver server_receiver;

   server_receiver.set_handler<query>(
      [&server_sender](const query& q) {
         std::cout
         << "from client:" << std::endl
         << "   msg : " << q.message << std::endl
         << "   code: " << q.code << std::endl;
         server_sender.send(result("Ok", 1));
      }
   );

   /**  */
   client_sender.raw_send = std::bind(&rpc_receiver::dispatch, &server_receiver, std::placeholders::_1);
   server_sender.raw_send = std::bind(&rpc_receiver::dispatch, &client_receiver, std::placeholders::_1);

   /**  */
   client_sender.send(query("query", 3));
}

/***************************************************************************/


Автор: mes 29.10.2010, 00:00
теперь осталось сделать контроль пропуска сообщений, чтоб нельзя было отправить чужое сообщение, но уже сейчас можно запихнуть полученое в rpc_parts.h и на этой основе строить клиента..

Автор: boostcoder 29.10.2010, 00:44
если я ничего не напутал, то здесь ошибочка.
в rpc_pack вы не пакуете идентификатор. но при распаковке пакета, нам не известен его идентификатор, и в функции rpc_unpack вы его не распаковываете. или я опять что-то напутал?

Автор: boostcoder 29.10.2010, 02:50
вот что у меня получилось:
Код

#include <iostream>
#include <functional>
#include <array>
#include <memory>
#include <map>
#include <sstream>

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

/***************************************************************************/

namespace types {
   enum { header_length = 12 };
   static const char* format = "%02d%04d%06d";
   
   typedef std::string rpc_packet;
   
   size_t get_class_id(const rpc_packet& pack) {
      size_t id = 0, unused1, unused2;
      sscanf(pack.data(), format, &id, &unused1, &unused2);
      return id;
   }

   size_t get_meta_id(const rpc_packet& pack) {
      size_t id = 0, unused1, unused2;
      sscanf(pack.data(), format, &unused1, &id, &unused2);
      return id;
   }
   
   size_t get_body_len(const rpc_packet& pack) {
      size_t unused1, unused2, len = 0;
      sscanf(pack.data(), format, &unused1, &unused2, &len);
      return len;
   }

   template<typename T, typename C>
   std::string encode_header(const std::string& body) {
      std::string tmp(header_length, 0);
      sprintf(&tmp[0], format, C::class_id, T::meta_id, body.length());
      return tmp;
   }

   template<typename T, typename C>
   rpc_packet rpc_pack(const T& o) {
      std::ostringstream os;
      boost::archive::text_oarchive(os) << o;
      rpc_packet pack = encode_header<T, C>(os.str()) + os.str();
      std::cout << "rpc_pack::pack: " << pack << std::endl;
      return pack;
   }
   template<typename T>
   T rpc_unpack(const rpc_packet& pack) {
      T type;
      std::cout << "rpc_unpack::pack: " << pack << std::endl;
      std::istringstream is(std::string(pack.begin()+header_length, pack.end()));
      boost::archive::text_iarchive(is) >> type;
      return type;
   }
};

/***************************************************************************/

struct rpc_sender {
   std::function<void(const types::rpc_packet&)> raw_send;
   rpc_sender() {}

   template<typename T, typename C>
   void send(const T& o) {
      raw_send(types::rpc_pack<T, C>(o));
   }
};

/***************************************************************************/

struct rpc_receiver {
   rpc_receiver() {}
   
   struct i_invoker {
      virtual void dispatch(const types::rpc_packet&) = 0;
   };
   typedef std::shared_ptr<i_invoker> i_invoker_ptr;

   template<typename T>
   struct invoker;
   
   void dispatch(const types::rpc_packet& pack) {
      auto it = _map.find(types::get_meta_id(pack));
      if ( it == _map.end() || !it->second ) {
         throw std::runtime_error(" if 'rpc_receiver::dispatch()' bad ID");
      }
      it->second->dispatch(pack);
   }

   template<typename T>
   void set_handler(typename invoker<T>::func_t f) {
      _map[T::meta_id] = i_invoker_ptr(new invoker<T>(f));
   }

private:
   std::map<int, i_invoker_ptr> _map;
};

template<typename T>
struct rpc_receiver::invoker: i_invoker {
   typedef std::function<void(const T&)> func_t;

   invoker(func_t& f):m_func(f){}

   virtual void dispatch(const types::rpc_packet& pack) {
      m_func(types::rpc_unpack<T>(pack));
   }
   
private:
   func_t m_func;
};

/***************************************************************************/

struct query1 {
   query1() {}
   query1(const std::string& s, int c):message(s),code(c) {}

   enum { meta_id = 0 };
   std::string message;
   int code;

private:
   friend class boost::serialization::access;
   template<typename archive_type>
   void serialize(archive_type& ar, const unsigned int) {
      ar & message
         & code;
   }
};

struct query2 {
   query2() {}
   query2(const std::string& s, int c):message(s),code(c) {}

   enum { meta_id = 0 };
   std::string message;
   int code;

private:
   friend class boost::serialization::access;
   template<typename archive_type>
   void serialize(archive_type& ar, const unsigned int) {
      ar & message
         & code;
   }
};

struct result1 {
   result1() {}
   result1(const std::string& s, int c):message(s),code(c) {}
   
   enum { meta_id = 1 };
   std::string message;
   int code;

private:
   friend class boost::serialization::access;
   template<typename archive_type>
   void serialize(archive_type& ar, const unsigned int) {
      ar & message
         & code;
   }
};

struct result2 {
   result2() {}
   result2(const std::string& s, int c):message(s),code(c) {}
   
   enum { meta_id = 1 };
   std::string message;
   int code;

private:
   friend class boost::serialization::access;
   template<typename archive_type>
   void serialize(archive_type& ar, const unsigned int) {
      ar & message
         & code;
   }
};

/***************************************************************************/

struct i_server_impl {
   enum { class_id = 0 };
   virtual void method(const query1&) = 0;
};

struct server_impl: i_server_impl {
   virtual void method(const query1& q) {
      std::cout << "impl1::method" << std::endl;
   }
};

struct i_client_impl {
   enum { class_id = 1 };
   virtual void method(const query2&) = 0;
};

struct client_impl: i_client_impl {
   virtual void method(const query2& q) {
      std::cout << "impl2::method" << std::endl;
   }
};

/***************************************************************************/

struct client {
   client() {
      receiver.set_handler<result1>(
         [](const result1& r) {
            std::cout
            << "from server:" << std::endl
            << "   msg : " << r.message << std::endl
            << "   code: " << r.code << std::endl;
         }
      );
   }

   rpc_sender sender;
   rpc_receiver receiver;
};

struct server {
   server() {
      receiver.set_handler<query1>(
         [&sender](const query1& q) {
            std::cout
            << "from client:" << std::endl
            << "   msg : " << q.message << std::endl
            << "   code: " << q.code << std::endl;
            sender.send<result1, client_impl>(result1("Ok", 1));
         }
      );
   }

   rpc_sender sender;
   rpc_receiver receiver;
};

/***************************************************************************/

int main() {
   client client;
   server server;

   client.sender.raw_send = std::bind(&rpc_receiver::dispatch, &server.receiver, std::placeholders::_1);
   server.sender.raw_send = std::bind(&rpc_receiver::dispatch, &client.receiver, std::placeholders::_1);

   client.sender.send<query1, server_impl>(query1("query1", 3));
}

/***************************************************************************/

http://liveworkspace.org/code/487c740c8d6835ca0d30f827a3ef6efc

Автор: boostcoder 29.10.2010, 03:10
насколько я понимаю, указатели на классы обработчики должны находится в карте клиента и сервера.
но создавать их в конструкторе клиента и сервера - нехорошо.. во первых  - жестко завязано. во вторых - это может мешать архитектуре клиента и сервера.
мне кажется логичным, создавать классы-обработчики по запросу, как я уже писал.

с утра уже..

Автор: boostcoder 29.10.2010, 03:47
вот: http://liveworkspace.org/code/e86552b94c9548c9d464261d3bd7d8cf
с обработчиками в виде классов.
вот только в данный момент нет возможности из обработчика сделать вызов...

Добавлено через 2 минуты и 17 секунд
нужно утвердить код, и вынести его в отдельный хидер, а то уже прокручивать палец болит)

Автор: mes 29.10.2010, 08:18
Цитата(boostcoder @  28.10.2010,  23:44 Найти цитируемый пост)
в rpc_pack вы не пакуете идентификатор. но при распаковке пакета, нам не известен его идентификатор, и в функции rpc_unpack вы его не распаковываете. или я опять что-то напутал? 

21я строчка в моем последнем наброске smile
а в unpack нам не нужен идентификатор, у нас там уже известен тип smile

Автор: mes 29.10.2010, 09:01
Цитата(boostcoder @  29.10.2010,  01:50 Найти цитируемый пост)
static const char* format = "%02d%04d%06d";

и все остальное относящееся к этому зачем ?! 
откуда такие предположения, что при отправки по сети подойдет этот формат.. зачем вобще привязываться к нему..
заче не типозависимые данные паковать в строку ?! и много много еще камней, которым Вы так и пытаетесь забросать ручей логики кода.. 
 smile

Добавлено @ 09:04
Цитата(boostcoder @  29.10.2010,  02:10 Найти цитируемый пост)
мне кажется логичным, создавать классы-обработчики по запросу, как я уже писал.

вот опять опережаете.. у Вас не сформирован скелет проги, а Вы уже тяжести поднимать заставляете smile

Добавлено @ 09:07
Код

    throw std::runtime_error(" if 'rpc_receiver::dispatch()' bad ID");

bad_ID будет при проверке среди возможных ид_пакетов.. А так это всего лишь не зарегистрированный handler smile

Добавлено @ 09:11
Цитата(boostcoder @  29.10.2010,  02:47 Найти цитируемый пост)
с обработчиками в виде классов.

ну и толку от такой конструкции ? так и в первый ресивер  можно было вместо лямбды пробиндить метод с объектом..

Добавлено @ 09:14
Цитата(boostcoder @  29.10.2010,  02:47 Найти цитируемый пост)
нужно утвердить код, и вынести его в отдельный хидер,

утвержденый код (точнее его база) в сообщении предшествующее этому :
Цитата(mes @  28.10.2010,  23:00 Найти цитируемый пост)
теперь осталось сделать контроль пропуска сообщений, чтоб нельзя было отправить чужое сообщение, но уже сейчас можно запихнуть полученое в rpc_parts.h и на этой основе строить клиента..

smile

Автор: mes 29.10.2010, 09:18
Код

   client.sender.send<query1, server_impl>(query1("query1", 3));

 smile  smile  smile  smile 

Вы, как клиент, за сервер решили, каким образом ему обрабатывать свои сообщения ?!
 smile 



Автор: boostcoder 29.10.2010, 12:50
хорошо.
какая часть того кода остается неизменной, чтоб ее вынести в отдельный файл?

Автор: mes 29.10.2010, 12:56
Цитата(boostcoder @  29.10.2010,  11:50 Найти цитируемый пост)
какая часть того кода остается неизменной, чтоб ее вынести в отдельный файл

не неизменной, а опорной.. все кроме маин.. 

Автор: boostcoder 29.10.2010, 13:07
ок. закинул его в отдельный файл.
далее используем так:
Код

// http://liveworkspace.org/code/aa539e62ca1c9439d0017fe1a180288b
#include <rpc-rev1.hpp>

int main() {
   /** client */
   rpc_sender client_sender;
   rpc_receiver client_receiver;

   client_receiver.set_handler<result>(
      [](const result& r) {
         std::cout
         << "from server:" << std::endl
         << "   msg : " << r.message << std::endl
         << "   code: " << r.code << std::endl;
      }
   );

   /** server */
   rpc_sender server_sender;
   rpc_receiver server_receiver;
   server_receiver.set_handler<query>(
      [&server_sender](const query& q) {
         std::cout
         << "from client:" << std::endl
         << "   msg : " << q.message << std::endl
         << "   code: " << q.code << std::endl;
         server_sender.send(result("Ok", 1));
      }
   );
   /**  */
   client_sender.raw_send = std::bind(&rpc_receiver::dispatch, &server_receiver, std::placeholders::_1);
   server_sender.raw_send = std::bind(&rpc_receiver::dispatch, &client_receiver, std::placeholders::_1);
   /**  */
   client_sender.send(query("query", 3));
}

http://liveworkspace.org/code/93eb6d4eba45dd51bfdc9ede71f34864

Автор: mes 29.10.2010, 13:11
Цитата(boostcoder @  29.10.2010,  12:07 Найти цитируемый пост)
ок. закинул его в отдельный файл.

 smile 

терь в какую строну двигаемся ? готов выслушать пожелания smile

Автор: boostcoder 29.10.2010, 13:12
smile 
я думаю что пора реализовать возможность создания обработчиков по запросу сторон. нет?

Добавлено через 4 минуты и 18 секунд
еще момент... пишу..

Автор: mes 29.10.2010, 13:19
Цитата(boostcoder @  29.10.2010,  12:12 Найти цитируемый пост)
я думаю что пора реализовать возможность создания обработчиков по запросу сторон. нет?

а мне кажется для начала нужно реализовать нормальное  клиент серверное взаимодействие на чтоб прочуствовать, что у нас есть, а чего не хватает.. 

Автор: boostcoder 29.10.2010, 13:27
я переделываю кодогенератор так, чтоб записывать обработчики следующим образом:
Код

CLASS_BEGIN(test)
   DECLARE_METHOD(int, method1, const std::string&, int, const char*)
   DECLARE_METHOD(void, method2, const std::std::vector<int>&, int)
CLASS_END()

этот способ мне кажется во много раз более понятным, чем, к примеру, прежний:
Код

DECLARE_HANDLER_CLASS(
   // abstract class name
   registration_api,
   // check if user with this nick and email already exists
   ((user_exists,
      ((query,
        ((std::string, nick))
        ((std::string, email))
      ))
      ((result,
         ((int, code))
         ((std::string, message))
      ))
   ))
   // registration query
   ((registration,
      ((query,
         ((std::string, nick))
         ((std::string, email))
      ))
      ((result,
         ((std::string, message))
         ((int, code))
      ))
   ))
)



исходя из этого, предполагаю использовать это так:
Код

client client(...);
remote_object<test> object = client.create<test>();
object->method1(...);
object->method2(...);


как вам такой вариант?
какие мысли по этому поводу?

но тут есть одно "но"! в таком случае, у нас нет типов команд(по крайней мере явных). т.е. можно кодогенератор реализовать так, чтоб из аргументов методов создавал структуру и присваивал ей ID. дальше не знаю как и что..

Добавлено через 35 секунд
Цитата(mes @ 29.10.2010,  13:19)
Цитата(boostcoder @  29.10.2010,  12:12 Найти цитируемый пост)
я думаю что пора реализовать возможность создания обработчиков по запросу сторон. нет?

а мне кажется для начала нужно реализовать нормальное  клиент серверное взаимодействие на чтоб прочуствовать, что у нас есть, а чего не хватает..

реализовать сетевое взаимодействие?

Автор: mes 29.10.2010, 13:29
Цитата(boostcoder @  29.10.2010,  12:27 Найти цитируемый пост)
я переделываю кодогенератор так, чтоб записывать обработчики следующим образом:

ну вот опять... рано силы тратите.. 

Автор: boostcoder 29.10.2010, 13:30
Цитата(mes @ 29.10.2010,  13:29)
Цитата(boostcoder @  29.10.2010,  12:27 Найти цитируемый пост)
я переделываю кодогенератор так, чтоб записывать обработчики следующим образом:

ну вот опять... рано силы тратите..

ок.
на потом.

Автор: mes 29.10.2010, 13:31
Цитата(boostcoder @  29.10.2010,  12:27 Найти цитируемый пост)
реализовать сетевое взаимодействие? 

ага.. чтоб на примере посмотреть, что у нас получилось..

после попробуем реализовать обработчики удобным способом 

и только после этого можно будет подумать о кодогенераторе..

Добавлено через 56 секунд
я тут пока с mpl разбираюсь..  интересная штуковина .. и нам как раз будет кстати smile

Автор: boostcoder 29.10.2010, 13:33
хорошо. пишу.

Добавлено через 1 минуту и 57 секунд
Цитата(mes @  29.10.2010,  13:31 Найти цитируемый пост)
я тут пока с mpl разбираюсь..  интересная штуковина .. и нам как раз будет кстати

на boost.fusion взгляните.

Автор: mes 29.10.2010, 13:37
для начала напишите в таком прообразе :
Код

struct client
{
     template <class T>
     void send (T& o) { sender.send(o); }
     template<class T>
     void set_handler.. 
     ...
     rpc_sender    sender;
     rpc_receiver   receiver;
};



Автор: boostcoder 29.10.2010, 13:49
ок.

Автор: mes 29.10.2010, 13:52
Цитата(boostcoder @  29.10.2010,  12:33 Найти цитируемый пост)
boost.fusion взгляните. 

если я правильно представляю фусион, то он не подходит, так как
мне нужен не контейнер разнотипных данных, а контейнер разных типов и операции над ним smile

Добавлено через 1 минуту и 59 секунд
Цитата

Fusion is a library for working with heterogenous collections of data, commonly referred to as tuples

посмотрел, точно для текущих планов  не то.. 

Автор: boostcoder 29.10.2010, 14:12
Цитата(mes @  29.10.2010,  13:52 Найти цитируемый пост)
не контейнер разнотипных данных, а контейнер разных типов

не понял..

Добавлено через 38 секунд
вообще-то во fusion несколько типов контейнеров.

Автор: mes 29.10.2010, 14:14
Цитата(boostcoder @  29.10.2010,  13:12 Найти цитируемый пост)
вообще-то во fusion несколько типов контейнеров. 

в любом случае их цель хранить данные..  а мне не нужны сейчас данные.. мне нужны пустые типы. 
smile

Автор: boostcoder 29.10.2010, 14:27
я создавал карту типов так:
Код

#include <boost/fusion/container/map.hpp>
#include <boost/fusion/include/map.hpp>
#include <boost/fusion/container/map/map_fwd.hpp>
#include <boost/fusion/include/map_fwd.hpp>
#include <boost/mpl/int.hpp>
#include <boost/fusion/support/pair.hpp>
#include <boost/fusion/include/pair.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <iostream>

struct type1 {
   enum { id = 0 };
};

struct type2 {
   enum { id = 1 };
};

struct type3 {
   enum { id = 2 };
};

namespace bf = boost::fusion;
namespace bm = boost::mpl;

struct printer {
   template<typename T>
   void operator()(const T& o) const {
      std::cout << o.second.id << std::endl;
   }
};

int main() {
   typedef bf::map<
      bf::pair<bm::int_<0>, type1>,
      bf::pair<bm::int_<1>, type2>,
      bf::pair<bm::int_<2>, type3>
   > types;
   types t;
   bf::for_each(t, printer());
}


http://liveworkspace.org/code/26d4922d44f6e2639c66cd6f41aa7257

Добавлено через 1 минуту и 3 секунды
Цитата(mes @  29.10.2010,  14:14 Найти цитируемый пост)
в любом случае их цель хранить данные..  а мне не нужны сейчас данные.. мне нужны пустые типы

ну да.. тогда mpl smile

Добавлено через 1 минуту и 41 секунду
значит код приведенный выше не актуален.

Автор: boostcoder 29.10.2010, 16:26
в связи с тем, что мне сейчас нужно реализовать передачу/прием пакетов по сети, и как следствие, паковать и распаковывать их, возник следующий вопрос:
пакету нужен заголовок для инфы о нем(id типа, и размер тела). при этом, размер заголовка неизменен. так вот.. для десериализации и диспетчеризации пакета при приеме, нам нужно извлечь из него id и размер тела. при том, размер тела нам так же нужен для чтения тела из сети, т.к. его размер не константен.
собственно сам вопрос: нам нужны два типа: 1) packet_header, 2) packet_data. так?
и еще вопрос: с packet_header - понятно, две целых. а как быть с packet_data? он ведь по сути просто строка. можно затайпдефить. или как?


лучше заранее спрошу..а то опять чего-то не так накожу)


Автор: mes 29.10.2010, 16:38
Цитата(boostcoder @  29.10.2010,  15:26 Найти цитируемый пост)
packet_header, 2) packet_data. так?

только они не связаны с rpc_packet.. 
пусть он будет net_packet  { header, data };
тут уже можно использовать константные буфферы и делать проверку если rpc_packet не помещается в net_packet
smile

Добавлено через 7 минут и 14 секунд
хотя наверно сама структура net_packet лишняя.. так как дата у нас и так есть...
серверу нужен общий буфер, и обоим хидер, или даже не хидер , а preamble
smile

Автор: boostcoder 29.10.2010, 17:16
Цитата(mes @  29.10.2010,  16:38 Найти цитируемый пост)
только они не связаны с rpc_packet.. 

а нам не нужно передавать/принимать rpc_packet::id ?

Добавлено через 50 секунд
опять не понимаю...

Автор: mes 29.10.2010, 17:29
Цитата(boostcoder @  29.10.2010,  16:16 Найти цитируемый пост)
опять не понимаю... 

вот так примерно может выглядит функция отправляющая в сокет :
Код

void net_send (socket& sock, rpc_packet const& pack )
{
    char preamble[12];  sprintf (premble, "...", pack.raw_data.size(), pack.id );
    sock.write (preamble, 12);
    sock.write ( pack.raw_data.c_str(), pack.raw_data.size());   
}

Автор: boostcoder 29.10.2010, 17:29
я, заголовок пакета вижу таким:
Код

[class id] - 2bytes
[method id] - 2bytes
[body size] - 6bytes
[body....]

тип пакета таким:
Код

struct rpc_packet {
   std::string raw_data;
};

все.
нам нужны две функции для упаковки типа в массив байт, и для распаковки. шаблонные.
так же, информационные аксесоры, такие как: get_class_id, get_method_id, get_body_size, get_raw_data

Автор: mes 29.10.2010, 17:30
Цитата(boostcoder @  29.10.2010,  16:29 Найти цитируемый пост)
[class id] - 2bytes
[method id] - 2bytes

какое это имеет отношение к нашим net_ или rpc_ ?!


Автор: boostcoder 29.10.2010, 17:31
Цитата(mes @  29.10.2010,  17:29 Найти цитируемый пост)
sock.write ( pack.raw_data.c_str(), pack.raw_data.id);   

т.е.
Код

sock.write(pack.raw_data.c_str(), pack.raw_data.length());
sock.write(&pack.raw_data.id, sizeof(pack.raw_data.id));

?

Автор: mes 29.10.2010, 17:31
Цитата(boostcoder @  29.10.2010,  16:29 Найти цитируемый пост)
тип пакета таким:

rpc_packet не трогать! разве что заменить raw_data на просто data.. или на просто raw..  чтоб не так длинно было..
 smile

Добавлено @ 17:33
Цитата(boostcoder @  29.10.2010,  16:31 Найти цитируемый пост)
sock.write(&pack.raw_data.id, sizeof(pack.raw_data.id));

нет.. Вы делаете зависимость от конкретной машины... использовать sizeof нельзя.. 


Автор: boostcoder 29.10.2010, 17:34
Цитата(mes @  29.10.2010,  17:30 Найти цитируемый пост)
какое это имеет отношение к нашим net_ или rpc_ ?!

в таком случае мы избавляемся от двух типов. остается только один.
порядок полей можно изменить, конечно.

Добавлено через 40 секунд
Цитата(mes @  29.10.2010,  17:31 Найти цитируемый пост)
нет.. Вы делаете зависимость от конкретной машины... использовать sizeof нельзя.. 

ок.

Автор: mes 29.10.2010, 17:46
Цитата(boostcoder @  29.10.2010,  16:34 Найти цитируемый пост)
в таком случае мы избавляемся от двух типов. остается только один.
порядок полей можно изменить, конечно.

второго типа пакета у нас и так нет, есть только преамбула.. а она отношение имеет только к сетевой передаче..
скрестив их, мало того что увеличиваете связанность, так еще своим предположением в рпц о преамбуле Вы связываете руки  net-"разработчику"
smile

Автор: boostcoder 29.10.2010, 17:48
ок.

Автор: boostcoder 29.10.2010, 19:08
Цитата(mes @  29.10.2010,  17:29 Найти цитируемый пост)
pack.raw_data.id

это что?

Автор: mes 29.10.2010, 19:10
с названиями у нас все таки не порядок.. но пока чего то не приходит как это исправить.. 
ладно пока подождем.. просто высказался. чтоб в голове не держать.. 

Автор: boostcoder 29.10.2010, 19:12
с pack.raw_data все понятно. но вот кто такой id в pack.raw_data ?

Автор: mes 29.10.2010, 19:12
Цитата(boostcoder @  29.10.2010,  18:08 Найти цитируемый пост)
это что? 

это опечатка, которая зародилась у меня, а Вы ее подмножили  smile 

Код

 sock.write ( pack.raw.c_str(), pack.raw.size() );   


Цитата(boostcoder @  29.10.2010,  18:12 Найти цитируемый пост)
но вот кто такой id в pack.raw_data

нет такого.. id сырья хранит пакет.. 
smile

Автор: boostcoder 29.10.2010, 19:19
понял.

Автор: boostcoder 29.10.2010, 20:30
еще момент..

в классе rpc_sender, в методе send есть такая конструкция:
Код

raw_send(rpc_pack<T>(o));

так вот... т.к. у нас сетевое взаимодействие асинхронное, нам нужно класть объекты в очередь пока они не "пошлются"  smile
сделал так:
Код

struct real_sender {
   real_sender(boost::asio::ip::tcp::socket& s, const rpc_packet& o)
      :_socket(s)
   {
      std::array<char, header_size> header;
      sprintf(header.data(), "%04d%06d", o.id, _packet.length());
      std::ostringstream os;
      os << header.data() << o.data;
      _packet = os.str();
   }

   void operator()() {
      boost::asio::async_write(_socket,
         boost::asio::buffer(_packet, _packet.length()),
         boost::bind(&real_sender::noop, this, boost::asio::placeholders::error)
      );
   }

private:
   void noop(const boost::system::error_code& e) {
      if ( e ) {
         std::cout << "error on sent packet: " << e.message() << std::endl;
      }
   }

private:
   boost::asio::ip::tcp::socket& _socket;
   std::string _packet;
};

//
struct rpc_sender {
   rpc_sender(boost::asio::ip::tcp::socket& s):_socket(s) {}

   template<typename T>
   void send(const T& o) {
      _socket.get_io_service().post(real_sender(_socket, rpc_pack(o)));
   }

private:
   boost::asio::ip::tcp::socket& _socket;
};


с этим все понятно. нас не интересует момент, когда пакет будет реально отправлен.

но вот с приемом пакетов есть нюанс.. объект, выполняющий чтение пакета, должен "оповещать" класс rpc_receiver о том, что пакет пришел. для этого, в него нужно передать указатель на метод или функциональный объект.

так сойдет?

Добавлено через 25 секунд
гляньте пожалуйста, с отправкой пакета все гуд?

Добавлено через 3 минуты и 2 секунды
код не проверял. могут быть ошибки.


для того, чтоб мы могли протестировать взаимодействие сетевой части в одном процессе, нужно выполнять asio::io_service::run в потоке. так что не удивляйтесь тому, что в классе клиента и сервера добавятся потоки.


Автор: mes 29.10.2010, 20:35
сейчас гляну..

а Вам если хотите отвлечься , посмотрите набросок :
http://liveworkspace.org/code/16a8c06a8c473fc51a16e5f2977fb7f6
на грязь не обращайте.. это черновик..


обратите внимание на класс distributor, и в особенности как идет доставка контролерам клиента.. 
 smile

Добавлено через 5 минут и 26 секунд
Цитата(boostcoder @  29.10.2010,  19:30 Найти цитируемый пост)
т.к. у нас сетевое взаимодействие асинхронное, нам нужно класть объекты в очередь пока они не "пошлются"

ага.. 

Цитата(boostcoder @  29.10.2010,  19:30 Найти цитируемый пост)
struct real_sender {
   real_sender(boost::asio::ip::tcp::socket& s, const rpc_packet& o)

ну если эта оболочка которая будет добавлена в очередь, то она не sender a holder.. 
а если это сендер, то он должен брать сообщение из очереди, а не хранить его.. 

сейчас дальше гляну..

Добавлено через 7 минут и 43 секунды
Цитата(boostcoder @  29.10.2010,  19:30 Найти цитируемый пост)
struct rpc_sender {
   rpc_sender(boost::asio::ip::tcp::socket& s):_socket(s) {}

ну зачем портить было rpc_sender ? он зарезервирован для другой функциональности...
если нужно что то для сети создавайте новые типы..

Добавлено через 9 минут и 7 секунд
а если боитесь лишних биндеров, они уйдут позже.. но нам они сейчас упрощают композицию..

Добавлено через 11 минут и 31 секунду
Цитата(boostcoder @  29.10.2010,  19:30 Найти цитируемый пост)
но вот с приемом пакетов есть нюанс.. объект, выполняющий чтение пакета, должен "оповещать" класс rpc_receiver о том, что пакет пришел. для этого, в него нужно передать указатель на метод или функциональный объект.

да в любом случае он должен иметь ссылку, только вот на ресивер?, или все таки бинд к методу клиента?.

Добавлено через 14 минут и 1 секунду
Цитата(boostcoder @  29.10.2010,  19:30 Найти цитируемый пост)
для того, чтоб мы могли протестировать взаимодействие сетевой части в одном процессе,

вижу что тот простой трюк ( с биндами ) понравился smile
 smile  smile

Добавлено через 14 минут и 39 секунд
вроде все просмотрел.. если не на все ответил, уточните smile

Автор: boostcoder 29.10.2010, 20:51
Цитата(mes @  29.10.2010,  20:35 Найти цитируемый пост)
ну если эта оболочка которая будет добавлена в очередь, то она не sender a holder.. 
а если это сендер, то он должен брать сообщение из очереди, а не хранить его.. 

этот объект хранит в себе буфер который нужно послать. по этому, эти объекты мы кладем в очередь.

Цитата(mes @  29.10.2010,  20:35 Найти цитируемый пост)
она не sender a holder.. 

в контексте программирования это как переводится?

Цитата(mes @  29.10.2010,  20:35 Найти цитируемый пост)
ну зачем портить было rpc_sender ? он зарезервирован для другой функциональности...
если нужно что то для сети создавайте новые типы..

Добавлено через 9 минут и 7 секунд
а если боитесь лишних биндеров, они уйдут позже.. но нам они сейчас упрощают композицию.. 

ок.

Автор: mes 29.10.2010, 20:53
Цитата(boostcoder @  29.10.2010,  19:51 Найти цитируемый пост)
в контексте программирования это как переводится?

держатель 

Автор: boostcoder 29.10.2010, 20:54
Цитата(mes @  29.10.2010,  20:35 Найти цитируемый пост)
только вот на ресивер?, или все таки бинд к методу клиента?.

мне кажется на ресивер. т.к. именно ресивер содержит карту инвокеров и метод dispatch()
его нужно вязать к rpc_receiver::dispatch(rpc_packet const& pack)

Добавлено через 1 минуту и 9 секунд
Цитата(mes @  29.10.2010,  20:53 Найти цитируемый пост)
держатель

ну тогда сложно определиться с его именем. т.к. он и держатель буфера который нужно отправить, и отправитель.

Добавлено через 1 минуту и 35 секунд
сейчас ваш код усваивать буду..

Автор: mes 29.10.2010, 20:55
Цитата(boostcoder @  29.10.2010,  19:51 Найти цитируемый пост)
этот объект хранит в себе буфер который нужно послать. по этому, эти объекты мы кладем в очередь.

почему не хранить объекты в очереди, а объекту давать ссылку на очередь..  это даст шанс к примеру удалить некоторые пакеты из очереди, если долго не отправляются... (понимаю что такая функциональность не нужна, пример приведен лишь в доказательство логичности предложенного подхода)

Добавлено через 1 минуту и 22 секунды
Цитата(boostcoder @  29.10.2010,  19:54 Найти цитируемый пост)
сейчас ваш код усваивать буду.. 

если вы пока под "впечатлением" клиента сервера, то не сбивайте настрой..
с другой стороны просмотр позволит заглянуть немного вперед.. я вижу у вас к сожалению нет пока общей картины, кк чему мы стремимся..

Добавлено через 2 минуты и 56 секунд
Цитата(boostcoder @  29.10.2010,  19:54 Найти цитируемый пост)
т.к. он и держатель буфера который нужно отправить, и отправитель.

нет..он не отправляет.. он его хранит и может отдать сокету.. который отправит..
а если б он брал из очереди и отдавал бы , то получился бы сендер..

Автор: boostcoder 29.10.2010, 21:00
Цитата(mes @  29.10.2010,  20:55 Найти цитируемый пост)
почему не хранить объекты в очереди, а объекту давать ссылку на очередь..

поясните..

Автор: mes 29.10.2010, 21:01
Цитата(boostcoder @  29.10.2010,  19:54 Найти цитируемый пост)
и метод dispatch()
его нужно вязать к rpc_receiver::dispatch(rpc_packet const& pack)

1.dispatch принимает рпц_пакет, а с сервера приходит чистый буфер..
заставлять делегата расшифровывать не хорошо.. так как могут потрбоваться знания которыми обладает только клиент..
2. диспатчеризироваться объект может более чем одному ресиверу (например на выбор) и только клиент знает кому и когда.. 

Автор: boostcoder 29.10.2010, 21:02
Цитата(mes @  29.10.2010,  20:55 Найти цитируемый пост)
нет..он не отправляет.. он его хранит и может отдать сокету.. который отправит..
а если б он брал из очереди и отдавал бы , то получился бы сендер..

значит holder

Добавлено через 2 минуты и 34 секунды
Цитата(mes @  29.10.2010,  21:01 Найти цитируемый пост)
заставлять делегата расшифровывать не хорошо.. так как могут потрбоваться знания которыми обладает только клиент..

нужен еще один тип? расшифровщик?
тогда нужен и шифровщик, который сейчас у меня в конструкторе real_sender

Автор: mes 29.10.2010, 21:06
Цитата(boostcoder @  29.10.2010,  20:00 Найти цитируемый пост)
почему не хранить объекты в очереди, а объекту давать ссылку на очередь..

поясните.. 

и даже не на очередь а на клиента.. 
и он даже не сендер , а write_action 

интуитивно лезет что от типа такого :
Код

struct write_action
{
    void operator () (..)  { if (m_client.has_what()) m_client.push_out_one(sock);

     ... & m_client;
};

хотя может действительно не клиент, а очередь.. трудно сказать.. делайте для начала на очередь..

Добавлено @ 21:07
Цитата(boostcoder @  29.10.2010,  20:02 Найти цитируемый пост)
нужен еще один тип? расшифровщик?
тогда нужен и шифровщик, который сейчас у меня в конструкторе real_sender 

скорее не типы, а функции, на подобиe pack/unpack 

сейчас вы шифровку сделаете в одном месте, а расшифровку в другом, потом еще где нибудь добавите.. а потом будете по всему коду искать почему они не симетричны..
а так у вас в одном месте и всегда можно оттестить отдельно от общей массы..

Добавлено @ 21:18
Цитата(mes @  29.10.2010,  20:06 Найти цитируемый пост)
интуитивно лезет что от типа такого :

а клиент после отправки асио объекта с ссылкой, может быть уверен, что объект не будет жить дольше клиента ?

Автор: boostcoder 29.10.2010, 21:43
Цитата(mes @  29.10.2010,  21:06 Найти цитируемый пост)
а клиент после отправки асио объекта с ссылкой, может быть уверен, что объект не будет жить дольше клиента ?

в деструкторе io_service`а все объекты которые еще лежат в очереди будут разрушены.

или я не понял вопроса?

Добавлено через 3 минуты и 18 секунд
Цитата(mes @  29.10.2010,  21:06 Найти цитируемый пост)
и даже не на очередь а на клиента.. 
и он даже не сендер , а write_action 

интуитивно лезет что от типа такого :
код C++
struct write_action
{
    void operator () (..)  { if (m_client.has_what()) m_client.push_out_one(sock);
     ... & m_client;
};

хотя может действительно не клиент, а очередь.. трудно сказать.. делайте для начала на очередь..

тут не понял...
какого типа ссылка на объект m_client?
что подразумевается по словом "клиент" ? и что подразумевается под словом "очередь" ?

Добавлено через 3 минуты и 47 секунд
Цитата(mes @  29.10.2010,  21:06 Найти цитируемый пост)
скорее не типы, а функции, на подобиe pack/unpack 

сейчас вы шифровку сделаете в одном месте, а расшифровку в другом, потом еще где нибудь добавите.. а потом будете по всему коду искать почему они не симетричны..
а так у вас в одном месте и всегда можно оттестить отдельно от общей массы..

это понял.

Автор: mes 29.10.2010, 21:55
Цитата(boostcoder @  29.10.2010,  20:43 Найти цитируемый пост)
в деструкторе io_service`а все объекты которые еще лежат в очереди будут разрушены.

ага..так и предполагал .. 

Цитата(boostcoder @  29.10.2010,  20:43 Найти цитируемый пост)
какого типа ссылка на объект m_client?

net_client



Цитата(boostcoder @  29.10.2010,  20:43 Найти цитируемый пост)
 и что подразумевается под словом "очередь" ?

к примеру стд::очередь рпц_пакетов..

к тому  же write_action может сбросить в сокет более одного пакета.. если есть время и место для этого .. 

Автор: boostcoder 29.10.2010, 22:00
Цитата(mes @  29.10.2010,  21:55 Найти цитируемый пост)
к примеру стд::очередь рпц_пакетов..

Добавлено через 1 минуту и 17 секунд
к тому write_action может сбросить в сокет более одного пакета.. если есть время и место для этого .. 

io_service и так представляет очередь. сам выполняет объекты которые в очереди. сам удаляет те, которые выполнил. размер очереди ограничивается только ресурсами системы.
зачем нам еще одна очередь?

Добавлено через 2 минуты и 9 секунд
а если мой вариант реал_сендера переделать так, чтоб он не выполнял упаковку рпц_пакета, чем он не устраивает?

Добавлено через 3 минуты и 10 секунд
Цитата(boostcoder @  29.10.2010,  22:00 Найти цитируемый пост)
мой вариант реал_сендера

ну и называть его холдером

Автор: mes 29.10.2010, 22:06
Цитата(boostcoder @  29.10.2010,  21:00 Найти цитируемый пост)
io_service и так представляет очередь. сам выполняет объекты которые в очереди. сам удаляет те, которые выполнил. размер очереди ограничивается только ресурсами системы.
зачем нам еще одна очередь? 

я не знал, и чего то не подумал о такой возможности..
 smile 
тогда  ссылка внутри в принципе не нужна..  а отправителя можно обозвать курьером или чем то в этом роде..

а чтоб пакет 100 раз не копировали, можно сделать его коровой... (CopyOnWrite)

но потом.. после того как все остальное закончим .. 

Автор: boostcoder 29.10.2010, 22:20
Цитата(mes @  29.10.2010,  22:06 Найти цитируемый пост)
я не знал, и чего то не подумал о такой возможности..

методом http://www.boost.org/doc/libs/1_44_0/doc/html/boost_asio/reference/io_service/post.html производится явная ставка объекта в очередь. все остальное что в asio асинхронное, работает таким-же образом, но не явно.

по поводу таймаутов на отправку: нет возможности удалить объект из очереди. но это реализуется выбросом исключения из объекта и повторным вызовом run() в catch блоке. читать http://www.boost.org/doc/libs/1_44_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.effect_of_exceptions_thrown_from_handlers. при том, io_service гарантирует, что все остальные объекты продолжат выполняться.

но это сейчас нам не нужно. и так работы еще огого)

Добавлено через 58 секунд
mes, скажите, чем именно мне сейчас заниматься?
а то все смешалось в кучу smile 

Автор: mes 29.10.2010, 22:23
Цитата(boostcoder @  29.10.2010,  21:20 Найти цитируемый пост)
 скажите, чем именно мне сейчас заниматься?
а то все смешалось в кучу   

ну вот сбил Вас.. :(

показывайте что у вас есть.. только не кучей..
для начала покажите как выглядит клиент.. 
smile

Автор: boostcoder 29.10.2010, 22:34
вот:
Код

struct net_client {
   net_client()
      :_service(),
      _socket(_service),
      _thread(new std::thread(&boost::asio::io_service::run, &_service)),
      _sender(_socket),
      _receiver(_socket)
   {}

   template<typename T>
   void send(const T& o) {
      _sender.send(o);
   }

private:
   boost::asio::io_service _service;
   boost::asio::ip::tcp::socket _socket;
   std::shared_ptr<std::thread> _thread;

   rpc_sender _sender;
   rpc_receiver _receiver;
};


Автор: mes 29.10.2010, 22:44
а пока закиньте плиз это в наш rpc_rev1.hpp
Код


template<class C, class A>
A& rpc_get_target(C&);

template <class C>
struct rpc_distributor
{
   void dispatch (rpc_packet const& pack)
   {
          auto it = m_map.find (pack.id);
          if (it != m_map.end() )
            it->second->dispatch (m_client, pack);
   }  
  
   struct i_invoker 
   {
      virtual void dispatch (client&, rpc_packet const&) =0;
   };
   typedef std::shared_ptr<i_invoker> i_invoker_ptr;
   
   template <class T, class A >
   struct invoker : i_invoker
   {
      typedef void (A::*mtd_t)(T const&);
      

      virtual void dispatch (C& c, rpc_packet const& pack)
      {
          (get_target<C, A>(c).*m_mtd)(rpc_unpack<T>(pack));
      }
      invoker(mtd_t mtd) : m_mtd(mtd) {}
      
      mtd_t m_mtd;
      
   };

   template <class T, class A >
   void set_handler (void (A::*mtd)(T const&) )
   {
       m_map[T::meta_id] = i_invoker_ptr (new invoker<T,A>(mtd) );
   }
   
   rpc_distributor (C &c) : m_client(c) {}
   C& m_client;
   
   std::map<int, i_invoker_ptr > m_map;
};


Добавлено @ 22:45
Цитата(boostcoder @  29.10.2010,  21:34 Найти цитируемый пост)
вот:

а где методы ?!

Автор: boostcoder 29.10.2010, 22:45
просто вставить в конец файла?

Добавлено через 1 минуту и 14 секунд
Цитата(mes @  29.10.2010,  22:44 Найти цитируемый пост)
а где методы ?!

во первых - я еще не закончил.
во вторых - мы с вами интенсивно общались последние два часа smile 

Автор: mes 29.10.2010, 22:46
Цитата(boostcoder @  29.10.2010,  21:45 Найти цитируемый пост)
просто вставить в конец файла? 

ага

Автор: boostcoder 29.10.2010, 22:47
кстати, а какие именно методы ему нужны? send() ведь есть)

Автор: mes 29.10.2010, 22:48
ну тогда и это чтоб не мешалось в другой файл ( test_ptcl.h)
Код

namespace ttt {
  struct conn  { enum {meta_id = 1000 }; 
                 void serialize (boost::archive::text_oarchive&, const unsigned int&) {}
                 void serialize (boost::archive::text_iarchive&, const unsigned int&) {}                 
               };
}

namespace sttc {
  struct ping  { enum {meta_id = 100 }; 
                 void serialize (boost::archive::text_oarchive&, const unsigned int&) {}
                 void serialize (boost::archive::text_iarchive&, const unsigned int&) {}                 
               }; 
  struct pong  { enum {meta_id = 101 };
                 void serialize (boost::archive::text_oarchive&, const unsigned int&) {}
                 void serialize (boost::archive::text_iarchive&, const unsigned int&) {}                 
               };     
}

namespace cts {
  struct user { enum {meta_id = 200 }; 
                void serialize (boost::archive::text_oarchive&, const unsigned int&) {}
                void serialize (boost::archive::text_iarchive&, const unsigned int&) {}                
              };
  struct quit { enum {meta_id = 201 }; 
                void serialize (boost::archive::text_oarchive&, const unsigned int&) {}
                void serialize (boost::archive::text_iarchive&, const unsigned int&) {}                
              };
  
}
namespace stc {
  struct welcome { enum {meta_id = 500 };
                   void serialize (boost::archive::text_oarchive&, const unsigned int&) {} 
                   void serialize (boost::archive::text_iarchive&, const unsigned int&) {}                     
                 };
}



Добавлено через 1 минуту и 2 секунды
Цитата(boostcoder @  29.10.2010,  21:47 Найти цитируемый пост)
кстати, а какие именно методы ему нужны? send() ведь есть) 

под методами я имел ввиду ваши биндеры и ассинхронные агенты..

Добавлено через 3 минуты и 33 секунды
помимо send , нужен еще set_handler ресивера.. а также connect, и disconnect.. 


Автор: boostcoder 29.10.2010, 22:55
Цитата(mes @  29.10.2010,  22:46 Найти цитируемый пост)
ага

вставил

Добавлено через 1 минуту и 45 секунд
Цитата(mes @  29.10.2010,  22:48 Найти цитируемый пост)
ну тогда и это чтоб не мешалось в другой файл ( test_ptcl.h)

в этот файл нужно заинклудить rpc-rev1.hpp ? или наоборот? или ничего не инклудить?

Автор: mes 29.10.2010, 23:01
ой сорри.. я там оказывается в дистрибьютере забыл поменять get_target, на rpc_get_target..

Добавлено через 2 минуты и 7 секунд
Цитата(mes @  29.10.2010,  21:44 Найти цитируемый пост)
  virtual void dispatch (client&, rpc_packet const&) =0;

и тут плиз client на C

Автор: boostcoder 29.10.2010, 23:03
Цитата(mes @  29.10.2010,  22:48 Найти цитируемый пост)
под методами я имел ввиду ваши биндеры и ассинхронные агенты..

это то, что я предлагал http://forum.vingrad.ru/index.php?showtopic=311688&view=findpost&p=2239176 посте?
что в них изменить?
и как их назвать? send_holder и receive_holder ?

Автор: mes 29.10.2010, 23:08
Цитата(boostcoder @  29.10.2010,  22:03 Найти цитируемый пост)
send_holder и receive_holder 

давайте пока остановимся на :
Код

outgoing 
incomming


Добавлено через 40 секунд
или даже сократить
Код

outgo
incom

Автор: boostcoder 29.10.2010, 23:10
Цитата(mes @  29.10.2010,  23:01 Найти цитируемый пост)
забыл поменять get_target, на rpc_get_target..

он так и зовется:
Код

template<class C, class A>
A& rpc_get_target(C&);


Цитата(mes @  29.10.2010,  23:01 Найти цитируемый пост)
и тут плиз client на C

сделал.

файл test_ptcl.h тоже.

Добавлено через 2 минуты и 9 секунд
Цитата(mes @  29.10.2010,  23:08 Найти цитируемый пост)
или даже сократить
outgo
incom

что в них изменить, помимо выноса функций упаковки/распаковки ?

Автор: mes 29.10.2010, 23:13
Цитата(boostcoder @  29.10.2010,  22:10 Найти цитируемый пост)
он так и зовется:

его я поменял, а вот в invoker::dispatch забыл :(

Автор: boostcoder 29.10.2010, 23:20
Цитата(mes @  29.10.2010,  23:13 Найти цитируемый пост)
его я поменял, а вот в invoker::dispatch забыл :(

сделал.

Автор: mes 29.10.2010, 23:20
Цитата(boostcoder @  29.10.2010,  22:03 Найти цитируемый пост)
что в них изменить?

я видел только outgo (real_sender), a incoma не было ..

Добавлено @ 23:22
Цитата(boostcoder @  29.10.2010,  22:20 Найти цитируемый пост)
сделал. 

спасибо,

терь хоть рассмотреть можно.. не крутя километры.. 

http://liveworkspace.org/code/faebea2999c743443b75f45f1100a42b

Автор: boostcoder 29.10.2010, 23:23
Цитата(mes @  29.10.2010,  23:20 Найти цитируемый пост)
incoma не было .. 

так по поводу него я и спрашивал. и тут возникла дискуссия. продолжавшаяся до недавнего времени smile 
Цитата

но вот с приемом пакетов есть нюанс.. объект, выполняющий чтение пакета, должен "оповещать" класс rpc_receiver о том, что пакет пришел. для этого, в него нужно передать указатель на метод или функциональный объект.

так сойдет?


Добавлено через 1 минуту и 16 секунд
Цитата(mes @  29.10.2010,  23:20 Найти цитируемый пост)
терь хоть рассмотреть можно.. не крутя километры.. 

http://liveworkspace.org/code/50f5f88f2995...94083c87ae5c44d 

значит это мне к изучению. т.к. предыдущий код который я должен быть понять, я посмотрел только мельком, из-за дискуссии smile

Автор: mes 29.10.2010, 23:28
а я думал что вопрос решен :
Цитата(mes @  29.10.2010,  19:35 Найти цитируемый пост)
да в любом случае он должен иметь ссылку, только вот на ресивер?, или все таки бинд к методу клиента?.


Цитата(mes @  29.10.2010,  20:01 Найти цитируемый пост)
1.dispatch принимает рпц_пакет, а с сервера приходит чистый буфер..
заставлять делегата расшифровывать не хорошо.. так как могут потрбоваться знания которыми обладает только клиент..
2. диспатчеризироваться объект может более чем одному ресиверу (например на выбор) и только клиент знает кому и когда.. 


т.е. желательно связывать через делегата от клиента..

Добавлено через 1 минуту и 21 секунду
Цитата(boostcoder @  29.10.2010,  22:23 Найти цитируемый пост)
значит это мне к изучению. 

ну можно так сказать.. smile и тот кого Вы перенесли (дистрибьютор) тоже  smile 

Автор: boostcoder 29.10.2010, 23:32
Цитата(mes @  29.10.2010,  23:20 Найти цитируемый пост)
терь хоть рассмотреть можно.. не крутя километры.. 

http://liveworkspace.org/code/faebea2999c7...b75f45f1100a42b

скажите, а в этом коде, можно лямбду заменить на функции/методы? а то, честно говоря, я теряюсь из-за невозможности ясно понять кто вызывается smile 

Автор: mes 29.10.2010, 23:36
т важные моменты , на которые хотел бы обратить внимание, могу вкратце прокомментировать..

класс клиент (другой клиент, не те которые мы с вами знали, пользовательская часть) может состоять из разных разработчиков,
каждый из которых выполняет специализированную работу...
Код

struct client {

    auth_ctrl auth;
    ping_ctrl ping;

можно и динамически создавать их, но это пока не надо..
каждый обработчик может посылать пакеты, а также получать их.. 
при этом регистрация обработчиков в ресивере выглядит так 
Код

       receiver.set_handler (&auth_ctrl::on_welcome);
       receiver.set_handler (&ping_ctrl::on_signal);       
       receiver.set_handler (&ping_ctrl::on_echo); 
 
для выполнения метода хранится не инстанс соответсвующего методу типа, а только входная точка (т.е. наш клиент)

Добавлено через 39 секунд
Цитата(boostcoder @  29.10.2010,  22:32 Найти цитируемый пост)
скажите, а в этом коде, можно лямбду заменить на функции/методы? а то, честно говоря, я теряюсь из-за невозможности ясно понять кто вызывается 

лямбды на сервере.. их не смотрите.. смотрите только клиента.. 

Автор: boostcoder 29.10.2010, 23:47
Цитата(mes @  29.10.2010,  23:28 Найти цитируемый пост)
1.dispatch принимает рпц_пакет, а с сервера приходит чистый буфер..
заставлять делегата расшифровывать не хорошо.. так как могут потрбоваться знания которыми обладает только клиент..

с этим понял. вынесу в отдельные функции.


Цитата(mes @  29.10.2010,  23:28 Найти цитируемый пост)
2. диспатчеризироваться объект может более чем одному ресиверу (например на выбор) и только клиент знает кому и когда.. 
т.е. желательно связывать через делегата от клиента..

а это нет smile

Добавлено через 23 секунды
Цитата(mes @  29.10.2010,  23:36 Найти цитируемый пост)
лямбды на сервере.. их не смотрите.. смотрите только клиента.. 

хорошо.

Автор: mes 29.10.2010, 23:50
Цитата(boostcoder @  29.10.2010,  22:47 Найти цитируемый пост)
а это нет 

на случай если будет больше одного ресивера.. но не обращайте внимание, это опять с философской точки зрения, на практике может не пригодится.. я просто приводя для себя такие примеры определяю логичны ли метод и его месторасположение..

Добавлено @ 23:56
что то мне кажется, то что Вы сейчас делаете, надо называть не нет клиентом, а rpc_tcp_connection
и такие соединения подойдут для сервера..которые будут создаваться по приказу акцептора..

Добавлено @ 00:01
и еще добавьте флаг в  метод send
Код

void send (rpc_paket const& pack, bool extrn = true )
{ if (extrn) sender.send(pack);
  else       dispatch(pack); //  { reciever.dispatch(pack); }
}

 

Автор: boostcoder 30.10.2010, 00:10
Цитата(mes @  29.10.2010,  23:50 Найти цитируемый пост)
что то мне кажется, то что Вы сейчас делаете, надо называть не нет клиентом, а rpc_tcp_connection
и такие соединения подойдут для сервера..которые будут создаваться по приказу акцептора.. 

тут немного иначе все..

этой темой, я задался с целью изменить реализацию протокола. то, что я вам показывал до того как понял что оба участника сессии должны быть равноправными, не плохо работает, на практике. я такой принцип использовал в двух проектах.
но последний(текущий) потребовал от прежней реализации обратных колбэков и возможность создавать на сервере объекты по запросу клиента. собственно после этого я понял, что сетевое взаимодействие нужно переписывать с нуля. но логика проекта уже сформирована. и ее менять пока что нет причин.

процесс создания сессии пользователя:
сервер висит на аксепте. при подключении создается объект сессии пользователя, который существует до момента отключения пользователя. требуется возможность создавать объекты-обработчики по запросу клиента. объекты должны жить все время сессии, а так же, должна быть возможность удалить объект по запросу клиента.
объекты клиента - классы с методами обработчиками. классы могут иметь собственные данные члены. так же, должны поддерживаться создание обработчиков с передачей в их конструктор данных со стороны клиента.

это то, что мне предстоит реализовать.


Добавлено через 38 секунд
Цитата(mes @  29.10.2010,  23:50 Найти цитируемый пост)
и еще добавьте флаг в  метод send

это хорошая идея smile

Добавлено через 1 минуту и 12 секунд
итак.. нужно собраться с мыслями, и закодить что смогу, сегодня.

Добавлено через 3 минуты и 35 секунд
Цитата(boostcoder @  30.10.2010,  00:10 Найти цитируемый пост)
так же, должны поддерживаться создание обработчиков с передачей в их конструктор данных со стороны клиента.

т.е. нужна возможность, при запросе от клиента на создание объекта, так же передать и аргументы в конструктор.

Автор: mes 30.10.2010, 00:14
Цитата(boostcoder @  29.10.2010,  23:10 Найти цитируемый пост)
тут немного иначе все..

и как нижесказанное под этой цитатой противоречит вышесказанному ?


Автор: boostcoder 30.10.2010, 00:20
Цитата(mes @  30.10.2010,  00:14 Найти цитируемый пост)
и как нижесказанное под этой цитатой противоречит вышесказанному ?

не противоречит. просто аксептор и объект сессии уже есть.

Добавлено через 47 секунд
Цитата(mes @  29.10.2010,  23:50 Найти цитируемый пост)
которые будут создаваться по приказу акцептора..

ааа. я неправильно вас понял. сорри smile 

Автор: mes 30.10.2010, 00:21
Цитата(boostcoder @  29.10.2010,  23:10 Найти цитируемый пост)
т.е. нужна возможность, при запросе от клиента на создание объекта, так же передать и аргументы в конструктор. 

чувствую опять забегание далеко вперед smile

Автор: boostcoder 30.10.2010, 00:25
Цитата(mes @  30.10.2010,  00:21 Найти цитируемый пост)
чувствую опять забегание далеко вперед

ага smile
пошел изучать код..

Автор: boostcoder 30.10.2010, 00:43
Цитата(mes @  29.10.2010,  23:50 Найти цитируемый пост)
void send (rpc_paket const& pack, bool extrn = true )
{ if (extrn) sender.send(pack);
  else       dispatch(pack); //  { reciever.dispatch(pack); }
}

не могу понять, какого класса этот метод? того, который мне предстоит написать?

Добавлено через 1 минуту и 45 секунд
еще не знаю как расшифровать sttc
с cts и stc понятно.

Автор: mes 30.10.2010, 00:53
Цитата(boostcoder @  29.10.2010,  23:43 Найти цитируемый пост)
не могу понять, какого класса этот метод? того, который мне предстоит написать?

сейчас он называется net_client..

Автор: boostcoder 30.10.2010, 00:55
Цитата(mes @  30.10.2010,  00:53 Найти цитируемый пост)
сейчас он называется net_client..

а почему аргумент у него типа rpc_paket, а не T ?

Автор: mes 30.10.2010, 01:01
Цитата(boostcoder @  29.10.2010,  23:43 Найти цитируемый пост)
еще не знаю как расшифровать sttc

Код

stc  :  s  -> c
cts  :  c  -> s
sttc :  s <=> c
ctc  :  c <-> c

Автор: boostcoder 30.10.2010, 01:03
Цитата(mes @  30.10.2010,  01:01 Найти цитируемый пост)
sttc c<->s  

ага

Автор: mes 30.10.2010, 01:03
Цитата(boostcoder @  29.10.2010,  23:55 Найти цитируемый пост)
а почему аргумент у него типа rpc_paket, а не T ? 

ой сорри.. автоматом написал smile


Автор: boostcoder 30.10.2010, 01:09
ок.

Добавлено через 5 минут и 50 секунд
Цитата(mes @  29.10.2010,  23:50 Найти цитируемый пост)
//  { reciever.dispatch(pack); }

и то что в комментах, полагаю, нужно использовать?
т.е. если флаг == ложь, то диспетчеризируем заново? но тогда несоответствие типов. т.к. rpc_distributor::dispatch принимает rpc_paket

я опять не понял?

Автор: mes 30.10.2010, 01:22
Цитата(boostcoder @  30.10.2010,  00:09 Найти цитируемый пост)
я опять не понял? 

если флаг установлен, отправляет серверу, иначе себе.. 
потому как могут быть пакеты не предназначенные для сервера.. 
т.е. большой и малый круги кровопакетообращения.
хотя я рано это вам предложил.. опять запутал..

Добавлено через 52 секунды
Цитата(boostcoder @  30.10.2010,  00:09 Найти цитируемый пост)
. т.к. rpc_distributor::dispatch принимает rpc_paket

угу.. пока тогда оставьте..потом подумаем как .. 

Автор: boostcoder 30.10.2010, 01:24
Цитата(mes @  30.10.2010,  01:22 Найти цитируемый пост)
пока тогда оставьте..потом подумаем как .. 

ок.

Автор: mes 30.10.2010, 01:28
по идеи решается установкой двух сендеров, внешнего и локального.. 

Автор: mes 30.10.2010, 01:52
но  у такого прямого подхода есть недостаток в различном поведении внешних и внутренних отправлений...внутренний круг, будет не ассинхронный, и возможны рекурсивные вызовы.. поэтому вначале надо  реализовать этого клиента, чтоб понятно было .. 

Автор: boostcoder 30.10.2010, 01:53
реализовываю... но сейчас не закончу. с утра уже..

Автор: mes 30.10.2010, 01:56
Цитата(boostcoder @  30.10.2010,  00:53 Найти цитируемый пост)
реализовываю... но сейчас не закончу. с утра уже.. 

ок. спокойной ночи smile

Автор: boostcoder 30.10.2010, 02:02
и вам smile 

Автор: mes 30.10.2010, 10:51
сейчас вот отклонился на диспатчеризацию сообщения контроллерам.. в основу лег вчерашний дистрибьютор..
http://liveworkspace.org/code/aaf4c9421678bb4a09b522c0dbe5d4ef
чтоб не путаться рассматрение идет без внешних элементов ( в том числе сервера)
и для начала только один тип сообщения  простой - вызов... 



Автор: mes 30.10.2010, 11:21
теперь добавлена карта сообщений:
http://liveworkspace.org/code/93bbe9011487172275ee4e2130e216d3

в принципе эту часть можно считать законченной...
терь осталось посмотреть как прикрутить к сети.. 
и тогда останется сделать только контроль допуска сообщений в сендере, и сет_хандлере.. 
ну еще можно будет подумать, как орагнизовывать цепочку дистрибьютеров, но это уже не проблема..

Добавлено через 39 секунд
для сохраности выложу тут :
Код

#include <iostream>
#include <vector>
#include <map>
#include <memory>
#include <test_ptcl.hpp>
//....................................................................

template <typename C, typename A>
A& get_target (C& c);

template <typename C>
struct distributor
{
       template <typename T>
       void dispatch (T const &msg) 
       {
         auto p = m_map.find (T::meta_id);
         if (p != m_map.end())
            for (auto it = p->second.begin (); it != p->second.end(); ++it )
            {
                 (*it)->invoke(c, &msg);
            }
      }
      template <typename A, typename T>
      void add_handler ( void (A::*mtd)(T const& msg) )
      {
            m_map[T::meta_id].push_back(i_invoker_ptr (new invoker<A,T>(mtd)) ); 
      }
      struct i_invoker {
         virtual void invoke (C&, void const*) =0;               
      };
      typedef std::shared_ptr<i_invoker> i_invoker_ptr;
      
      template<typename A, typename T>
      struct invoker : i_invoker
      {
         typedef void (A::*mtd_t)( T const & );
         virtual void invoke (C& c, void const* )
         {
            (get_target<C,A>(c).*m_mtd)(T());
         }
         invoker (mtd_t mtd) : m_mtd(mtd) {}
      private:  
         mtd_t  m_mtd;
      };

      distributor(C& c_) : c(c_) {}
   private:     
      C& c;
      std::map<size_t, std::vector<i_invoker_ptr> > m_map;
};

//....................................................................
struct junction;

struct one_ctrl  {
      void fn (stc::welcome const&) { std::cout << ".one.fn()"<< std::endl; } 
      
      one_ctrl (junction& jn)  : m_junction(jn) {}      
   private:   
      junction &m_junction;
};
struct two_ctrl  
{
      void fn (stc::welcome const&) { std::cout << ".two.fn()"<<std::endl; } 
   
      two_ctrl (junction& jn)  : m_junction(jn) {}      
   private:   
      junction &m_junction;   
};
struct main_ctrl 
{
      void fn (stc::welcome const&) { std::cout << ".main.fn()"<<std::endl; } 

      main_ctrl (junction& jn)  : m_junction(jn) {}      
   private:   
      junction &m_junction;      
}; 

//....................................................................

struct junction {
 
    main_ctrl  main;
    one_ctrl   one;
    two_ctrl   two;
    
    void direct_call () {
      std::cout << "direct call:" << std::endl;       
      main.fn ( stc::welcome () );
      one.fn  ( stc::welcome () );
      two.fn  ( stc::welcome () );
    }
    
    void dispatched_call () {
      std::cout << "dispatched:" << std::endl;    
      m_distr.dispatch ( stc::welcome() ); 
   }
    
    junction () : main    (*this)
                , one     (*this)
                , two     (*this)
                , m_distr (*this)
    {
           
         m_distr.add_handler (&one_ctrl::fn);
         m_distr.add_handler (&two_ctrl::fn);
         m_distr.add_handler (&main_ctrl::fn);                 
    }    
    private:  
      distributor<junction>    m_distr;
};
// todo: убрать из глобальной видимости.. 
template <>
main_ctrl& get_target (junction& jn) { return jn.main; }
template <>
one_ctrl& get_target (junction& jn) { return jn.one; }
template <>
two_ctrl& get_target (junction& jn) { return jn.two; }


//....................................................................


int main ()
{
   junction ctrl;
   
   ctrl.direct_call();  
   ctrl.dispatched_call ();
}



Добавлено через 1 минуту и 29 секунд
ну и результат :
Цитата

direct call:
.main.fn()
.one.fn()
.two.fn()
dispatched:
.one.fn()
.two.fn()
.main.fn()


Добавлено через 14 минут и 12 секунд
хотя нет..  еще есть одна проблемка, для додумывания..
 сейчас get_target предполагает, что только один контролер каждого типа бывает.. 
 нужно добавить зависимость от статического ид агента/контролера..

Автор: mes 30.10.2010, 12:02
Цитата(mes @  30.10.2010,  10:21 Найти цитируемый пост)
хотя нет..  еще есть одна проблемка, для додумывания..
 сейчас get_target предполагает, что только один контролер каждого типа бывает.. 
 нужно добавить зависимость от статического ид агента/контролера..

вот в необлагороженном варианте :
http://liveworkspace.org/code/c1d2db5ef56341d2fc691b0a0e317e2c
Код

#include <iostream>
#include <vector>
#include <map>
#include <memory>
#include <test_ptcl.hpp>
//....................................................................

template <typename C, typename A, size_t CID>
struct get_target;


template <typename C>
struct distributor
{
       template <typename T>
       void dispatch (T const &msg) 
       {
         auto p = m_map.find (T::meta_id);
         if (p != m_map.end())
            for (auto it = p->second.begin (); it != p->second.end(); ++it )
            {
                 (*it)->invoke(c, &msg);
            }
      }
      template <size_t CID, typename A, typename T >
      void add_handler ( void (A::*mtd)(T const& msg) )
      {
            m_map[T::meta_id].push_back(i_invoker_ptr (new invoker<A,T>(mtd)) ); 
      }
      struct i_invoker {
         virtual void invoke (C&, void const*) =0;               
      };
      typedef std::shared_ptr<i_invoker> i_invoker_ptr;
      
      template<typename A, typename T, size_t CID=0>
      struct invoker : i_invoker
      {
         typedef void (A::*mtd_t)( T const & );
         virtual void invoke (C& c, void const* )
         {
            A& o  = get_target<C,A,CID>::get(c);
            
            (o.*m_mtd)(T());
         }
         invoker (mtd_t mtd) : m_mtd(mtd) {}
      private:  
         mtd_t  m_mtd;
      };

      distributor(C& c_) : c(c_) {}
   private:     
      C& c;
      std::map<size_t, std::vector<i_invoker_ptr> > m_map;
};

//....................................................................
struct junction;

struct one_ctrl  {
      void fn (stc::welcome const&) { std::cout << ".one.fn()"<< std::endl; } 
      
      one_ctrl (junction& jn)  : m_junction(jn) {}      
   private:   
      junction &m_junction;
};
struct two_ctrl  
{
      void fn (stc::welcome const&) { std::cout << ".two.fn()"<<std::endl; } 
   
      two_ctrl (junction& jn)  : m_junction(jn) {}      
   private:   
      junction &m_junction;   
};
struct main_ctrl 
{
      void fn (stc::welcome const&) { std::cout << ".main.fn()"<<std::endl; } 

      main_ctrl (junction& jn)  : m_junction(jn) {}      
   private:   
      junction &m_junction;      
}; 

//....................................................................

struct junction {
 
    one_ctrl   one;
    two_ctrl   two;
    main_ctrl  main;  
    two_ctrl   two1;
    
    void direct_call () {
      std::cout << "direct call:" << std::endl;       
      main.fn ( stc::welcome () );
      one.fn  ( stc::welcome () );
      two.fn  ( stc::welcome () );
      two1.fn ( stc::welcome () );
      std::cout << std::endl; 
    }
    
    void dispatched_call () {
      std::cout << "dispatched:" << std::endl;    
      m_distr.dispatch ( stc::welcome() ); 
      std::cout << std::endl;       
   }
    
    junction () : main    (*this)
                , one     (*this)
                , two     (*this)
                , two1    (*this)                
                , m_distr (*this)
    {
           
         m_distr.add_handler<0> (&one_ctrl::fn);
         m_distr.add_handler<0> (&two_ctrl::fn);
         m_distr.add_handler<0> (&main_ctrl::fn);                 
         m_distr.add_handler<1> (&two_ctrl::fn);
    }    
    private:  
      distributor<junction>    m_distr;
};
// todo: убрать из глобальной видимости.. 
template <>
struct get_target<junction, main_ctrl, 0> {
 static main_ctrl& get (junction& jn) { return jn.main; }
};
template <>
struct get_target<junction, one_ctrl, 0> {
 static one_ctrl& get (junction& jn) { return jn.one; }
};
template <>
struct get_target<junction, two_ctrl, 0> {
 static two_ctrl& get (junction& jn) { return jn.two; }
};
template <>
struct get_target<junction, two_ctrl, 1> {
 static two_ctrl& get (junction& jn) { return jn.two1; }
};



//....................................................................


int main ()
{
   junction ctrl;
   
   ctrl.direct_call();  
   ctrl.dispatched_call ();
}

а для доведения ума, уже  нужен будет mpl ..

Добавлено @ 12:10
boostcoder, и как время будет, добавьте плиз, в структуры тестового протокола пару дата_членов с сериализаций..
для тестирования..

Добавлено @ 12:16
и да кстати, как у нас там дела с клиентом.. не думаю что там столько дел, наверно опять не то пишете.. 
по идеи две структуры инком, оутго
пару фунцкий для связывания сигналов,
и парочка для связки с рпц..

думаю стоит показать smile



Автор: mes 30.10.2010, 14:05
начал ознакомление с буст::асио..  классная вещь - даже лучше , чем я  о ней думал.. 
и для теста нашего рпц_соединения на основе асио, нам даже сокет и сервер не нужен smile
сейчас только времени нет набросать пример.. ( 
smile

Автор: boostcoder 30.10.2010, 17:06
я тут smile 
в тестовые структуры вписал свойства и сериализацию.
далее..щас прочту новые посты.

Добавлено через 50 секунд
Цитата(mes @  30.10.2010,  14:05 Найти цитируемый пост)
и для теста нашего рпц_соединения на основе асио, нам даже сокет и сервер не нужен

какой-то shared объект использовать?

Добавлено через 6 минут и 8 секунд
у всех тестовых стркутур два свойства: 1) std::string : msg, 2) int : code
в конструкторах порядок такой: (std::string, int)

Автор: boostcoder 30.10.2010, 17:21
Цитата(mes @ 30.10.2010,  10:51)
сейчас вот отклонился на диспатчеризацию сообщения контроллерам.. в основу лег вчерашний дистрибьютор..
http://liveworkspace.org/code/aaf4c9421678bb4a09b522c0dbe5d4ef
чтоб не путаться рассматрение идет без внешних элементов ( в том числе сервера)
и для начала только один тип сообщения  простой - вызов...

тут все понятно.

Автор: mes 30.10.2010, 18:47
Цитата(boostcoder @  30.10.2010,  16:06 Найти цитируемый пост)
у всех тестовых стркутур два свойства: 1) std::string : msg, 2) int : code
в конструкторах порядок такой: (std::string, int) 

спс smile

Цитата(boostcoder @  30.10.2010,  16:06 Найти цитируемый пост)
какой-то shared объект использовать?

грубо говоря использовать асио просто как асинхронную очередь, без всяких сокетов.. 
smile

Добавлено через 18 секунд
сейчас дело как раз за ним..

Добавлено через 33 секунды
за асио всмысле.. 

Автор: boostcoder 30.10.2010, 18:54
Цитата(mes @  30.10.2010,  18:47 Найти цитируемый пост)
грубо говоря использовать асио просто как асинхронную очередь, без всяких сокетов.. 

я уже почти закончил с сокетами. давайте все же доведу эту часть до конца ;)

Автор: mes 30.10.2010, 18:57
Цитата(boostcoder @  30.10.2010,  17:54 Найти цитируемый пост)
я уже почти закончил с сокетами. давайте все же доведу эту часть до конца ;) 

а я и не предлагал забрасывать сокеты smile 
я  просто предложил еще один альтернативный вариант smile



Автор: boostcoder 30.10.2010, 19:26
гляньте пока класса inkom и outgo
Код

struct outgo {
   outgo(boost::asio::ip::tcp::socket& s, const rpc_packet& o)
      :_socket(s)
   {
      rpc_to_raw(_packet, o);
   }

   void operator()() {
      boost::asio::async_write(_socket,
         boost::asio::buffer(_packet, _packet.length()),
         boost::bind(&real_sender::noop, this, boost::asio::placeholders::error)
      );
   }

private:
   void noop(const boost::system::error_code& e) {
      if ( e ) {
         std::cout << "error on sent packet: " << e.message() << std::endl;
      }
   }

private:
   boost::asio::ip::tcp::socket& _socket;
   std::string _packet;
};

/** sender */
struct rpc_sender {
   rpc_sender(boost::asio::ip::tcp::socket& s):_socket(s) {}

   template<typename T>
   void send(const T& o) {
      _socket.get_io_service().post(outgo(_socket, rpc_pack(o)));
   }

private:
   boost::asio::ip::tcp::socket& _socket;
};

/***************************************************************************/

template<typename F>
struct inkom {
   inkom(boost::asio::ip::tcp::socket& s, const rpc_packet& p, F& f)
      :_socket(s)
      ,_packet(p),
      f(f)
   {}

   void operator()() {
      boost::asio::async_read(_socket,
         boost::asio::buffer(header.data(), header.size()),
         boost::bind(&real_receiver::header_is_read, this, boost::asio::palceholders::error)
      );
   }

private:
   void header_is_read(const boost::system::error_code& e) {
      if ( e ) {
         std::cout << "error on sent packet: " << e.message() << std::endl;
      } else {
         size_t id, len;
         sscanf(header.data(), format, &id, &len);
         _packet.id = id;
         _packet.data.resize(len);
         /**  */
         boost::asio::async_read(_socket,
            boost::asio::buffer(_packet.data, len),
            boost::bind(&real_receiver::body_is_read, this, boost::asio::palceholders::error)
         );
      }
   }

   void body_is_read(const boost::system::error_code& e) {
      if ( e ) {
         std::cout << "error on sent packet: " << e.message() << std::endl;
      } else {
         f(_packet);
      }
   }

private:
   boost::asio::ip::tcp::socket& _socket;
   rpc_packet& _packet;
   F f;
   std::array<char, header_size> header;
};

struct rpc_receiver {
   rpc_receiver(boost::asio::ip::tcp::socket& s)
      :_socket(s)
   {
      _socket.get_io_service().post(
         inkom(
            _socket,
            _packet,
            boost::bind(&rpc_receiver::dispatch, this)
         )
      );
   }

   struct i_invoker {
      virtual void dispatch(rpc_packet const &) = 0;
   };
   typedef std::shared_ptr<i_invoker> i_invoker_ptr;

   template<typename T>
   struct invoker;

   void dispatch(const rpc_packet& pack) {
      auto it  = _map.find(pack.id);
      if ( it != _map.end() && it->second )
         it->second->dispatch(pack);
   }

   template<typename T>
   void set_handler(typename invoker<T>::func_t f) {
      _map[T::meta_id] = i_invoker_ptr(new invoker<T>(f));
   }

private:
   boost::asio::ip::tcp::socket& _socket;
   std::map<int, i_invoker_ptr> _map;
   rpc_packet _packet;
};

с rpc_sender`ом и outgo вроде все гуд.
а вот с inkom и rpc_receiver`ом не все так гладко. что-то мне кажется что тут оверхеда полно..

Автор: mes 30.10.2010, 19:34
Цитата(boostcoder @  30.10.2010,  18:26 Найти цитируемый пост)
а вот с inkom и rpc_receiver`ом не все так гладко. что-то мне кажется что тут оверхеда полно.. 

делайте пока с оверхедом, потом лишнее выкинем.. ) 
код гляну позже сейчас ухожу..

Добавлено через 3 минуты и 19 секунд
не удержался, глянул
Цитата(boostcoder @  30.10.2010,  18:26 Найти цитируемый пост)
   struct i_invoker {
      virtual void dispatch(rpc_packet const &) = 0;
   };
   typedef std::shared_ptr<i_invoker> i_invoker_ptr;
   template<typename T>
   struct invoker;
   void dispatch(const rpc_packet& pack) {
      auto it  = _map.find(pack.id);
      if ( it != _map.end() && it->second )
         it->second->dispatch(pack);
   }
   template<typename T>
   void set_handler(typename invoker<T>::func_t f) {
      _map[T::meta_id] = i_invoker_ptr(new invoker<T>(f));
   }


что это за изобретательство в инкоме ?
это не его задача ! 

Автор: boostcoder 30.10.2010, 20:09
это не в инкоме. это в rpc_receiver

Автор: mes 30.10.2010, 20:14
Цитата(boostcoder @  30.10.2010,  19:09 Найти цитируемый пост)
это не в инкоме. это в rpc_receiver 

тогда что socket делает в ресивере ?! 
 smile

Добавлено через 2 минуты и 38 секунд
при получении событии на чтение, вызывается инком, который считывает хидер (или преамбулу) 
и тело, формирует rpc_packet и выбрасывает его .. 

больше ничего ему не нужно.. 

Автор: boostcoder 30.10.2010, 20:18
оффтоп
Цитата(mes @  30.10.2010,  14:05 Найти цитируемый пост)
начал ознакомление с буст::асио..  классная вещь - даже лучше , чем я  о ней думал..

еще гляньте блог автора asio: http://blog.think-async.com/
мне особенно понравилась его http://blog.think-async.com/2009/08/composed-operations-coroutines-and-code.html для использования совместно с asio. на тот случай, если вдруг понадобится имитировать несколько тысяч потоков.

Добавлено через 2 минуты и 28 секунд
Цитата(mes @  30.10.2010,  20:14 Найти цитируемый пост)
тогда что socket делает в ресивере ?! 

ну инкому ведь как-то нужно передать сокет.
ок. переделаю. но все равно в конструктор ресивера нужно передать сокет..

Добавлено через 4 минуты и 14 секунд
сокет все равно нужен. откуда инкомам передавать его?

Добавлено через 5 минут и 41 секунду
у сендера ведь есть сокет. почему его не должно быть у ресивера?

Добавлено через 10 минут и 16 секунд
Цитата(mes @  30.10.2010,  20:14 Найти цитируемый пост)
при получении событии на чтение, вызывается инком

чтоб получить событие чтения, нам нужно в сервайс впихнуть инком который и вернет rpc_packet в dispatch

Автор: mes 30.10.2010, 20:31
Цитата(boostcoder @  30.10.2010,  19:18 Найти цитируемый пост)
у сендера ведь есть сокет. почему его не должно быть у ресивера?

да? я не видел... а ему зачем, можно же связать биндом с нужной функцией ?

Добавлено через 1 минуту и 38 секунд
Цитата(boostcoder @  30.10.2010,  19:18 Найти цитируемый пост)
чтоб получить событие чтения, нам нужно в сервайс впихнуть инком который и вернет rpc_packet в dispatch 

ага.. он будет мостиком, по которому по событию из сокета прибудет наш пакет..

Добавлено через 4 минуты и 26 секунд
глянул на сендер.. Вы решили из за того что он пустой можно его заново переписать..а уже выше говорилось, что он сейчас пустой, дальеше в нем появиться функциональнасть позволающая отправлять только "прорегистированные"данные.. 

Автор: boostcoder 30.10.2010, 20:37
так...
конструктор инкома берет три аргумента: 1) сокет, 2) ссылку на rpc_packet(чтоб избежать копирования), 3) функциональный объект который будет выполнен по прочтению rpc_packet

что не так?

Автор: mes 30.10.2010, 20:37
из того, что сейчас находится в rpc-rev1.hpp ничего приспосабливать под знания асио не нужно..
если нужно что то, не бойтесь расплодить тип.. их потом легче срезать.. чем один тип разбивать на множество..

Добавлено через 3 минуты и 4 секунды
Цитата(boostcoder @  30.10.2010,  19:37 Найти цитируемый пост)
ссылку на rpc_packet(чтоб избежать копирования), 3) функциональный объект который будет выполнен по прочтению rpc_packet

функциональному объекту он будет передан по константной ссылке ? зачем тогда бояться копирования которого и так не будет.. 
я уже выше говорил оверхеда типа копирования на нашем этапе бояться не стоит.. это все легко чиститься по окончанию.. к тому же при  выровненной конструкции обычно даже и чистить нечего smile

Добавлено через 3 минуты и 32 секунды
остальное в написанном все так


Автор: boostcoder 30.10.2010, 20:41
Цитата(mes @  30.10.2010,  20:31 Найти цитируемый пост)
глянул на сендер.. Вы решили из за того что он пустой можно его заново переписать..а уже выше говорилось, что он сейчас пустой, дальеше в нем появиться функциональнасть позволающая отправлять только "прорегистированные"данные.. 

хорошо...

сендер мне нужен был чтоб реализовать net_client и net_server. ок. предположим что сендер уже не нужен. кто тогда будет отправлять команды?
еще вчера он был нужен...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)