Shadow RPC- легковесное RPC без использования CRT(c++)

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by sni4ok, 29 Aug 2007.

  1. sni4ok

    sni4ok Elder - Старейшина

    Joined:
    4 Nov 2006
    Messages:
    115
    Likes Received:
    37
    Reputations:
    12
    Доброе время суток форумчане.
    Сегодня я хочу поделиться с вами небольшой RPC библиотекой, которая поможет создавать
    крохотные клиент-серверные приложения, не использующие хоть и мощную, но увы тяжеловатую crt языка c++
    для примера, размер программы вида
    int main(){malloc(0);}
    получается порядка 30 киллобайт при статической линковки и полной оптимизации по размеру.
    Полный же размер crt может достигать в программе нескольких сот киллобайт.
    Разумеется для совсем небольших(в плане кода) программ это кажеться несколько накладно.
    Более того, следующие конструкции языка также неявно подгружают crt:
    исключения(увы и ах), полиморфизм, более того даже обычный operator new(nothrow) использует std::malloc, поэтому
    new и delete перегружены в этой либе, но и это ещё не всё! даже при включении некоторых стандартных хедеров подгружается crt
    например при инклуде <cassert>, размер exe'ика - релиза с оптимизацией на размер увеличивается с 1кб до 60.
    При использовании некоторых алгоритмов- также используется crt, например при std::copy.

    Данные наблюдения справедливы для MSVC8 и gcc(насчёт cassert'а для gcc не проверял), для других компиляторов не проверял,
    но сомневаюсь что у них в этом плане сильно лучше.
    Зато шаблоны и макросы доступны в полном обьёме:)

    Итак в типичном клиент-серверном взаимодействии(для простоты за сервер примем машину, на которой выполняются команды,
    которые сервер получает от клиента, разумеется в больших системах часто эти понятия могут быть переплетены, но тут я это не рассматриваю)
    есть прослойка отвечающая за отправку команд с клиента на сервер, и возращение результата работы выполнения этих команд назад.
    За суть команды в этой библиотеки я принял некоторую свободную функцию, которая получает параметры(до 10 штук) по значению,
    и возвращает некоторый результат(тоже по значению).
    Так как использование исключений не допустимо в этой библиотеке(иначе это уже не rpc без crt), а каждая удалённая функция
    может закончится провалом(например сервер повис, или просто провод обрубился) приходится вводить коды ошибок, любая rpc функция
    регистрируемая в библиотеке должна принимать первым значением - ссылку на код возврата, не нулевое значение которого
    означает, что выполнение функции завершилось неудачно, список ошибок можно посмотреть в enum'е shadow::Errors.
    Регистрация функций происходит в файле shandow_instance.h и формат регистрации следующий:
    enum FunctionType
    {
    //тут перечисляются некоторые идентификаторы функций, они используются исключительно внутри библиотеки и вы кроме
    //этого файла их нигде не увидете
    simple,
    fff,
    fcnt,

    //последний элемент опять таки используется в либе для определения колличества регистрируемых функий.
    last_function_id //this element must be in the end of enum
    };

    //дальше в этом же файле происходит регистрация самих функций, макросом SHADOW_FUNCTION
    SHADOW_FUNCTION(function_name, function_id, function_ret, array_of_arguments)

    где function_name -имя функции, которая будет вызываться удалённо(она должна быть в глобальном нэймспэйсе),
    function_id -идентификатор который вы написали ранее в FunctionType(для каждой функции должен быть строго 1 уникальный идентификатор)
    function_ret -тип возвращаемого значения функции
    array_of_arguments -список аргументов типов функции, при этом самый первый, обязательный параметр функции(ShadowRet по ссылке)
    в этот список не попадает, т.е запись
    SHADOW_FUNCTION(qwerty, fcnt, shadow::stl::string, (2,(shadow::stl::string, long long)))
    означает: регистрируем функцию с именем qwerty, идинтификатором fcnt, которая имеет прототип
    shadow::stl::string qwerty(shadow::ShadowRet& ret, shadow::stl::string str, long long value)

    для фундаментальных типов, а также для шаблонных классов shadow::stl::basic_string и shadow::stl::vector сериализацию прописывать не нужно,
    для любых других пользовательских типов придётся прописывать функции
    SerializeI и SerializeO, смысл их легко понять их исходников либы(файл shadow_rpc.hpp), но замечу что сериализация бинарная.

    //Рабочий пример
    файл shandow_instance.h:

    #ifndef SHADOW_FUNCTION_TYPE
    #define SHADOW_FUNCTION_TYPE

    enum FunctionType
    {
    simple,
    fff,
    fcnt,

    last_function_id //this element must be in the end of enum
    };

    #endif //SHADOW_FUNCTION_TYPE

    //multiply times header including

    SHADOW_FUNCTION(qwerty, fcnt, shadow::stl::string, (2,(shadow::stl::string, long long)))
    SHADOW_FUNCTION(funciyo, simple, int, (1, (int)))
    SHADOW_FUNCTION(ischo_funciya, fff, void, (0,()))


    //файл simple_server.cpp

    #include "stdafx.h"

    #ifndef _DEBUG
    #pragma comment(linker, "/SUBSYSTEM:WINDOWS")
    #pragma comment(linker, "/ENTRY:main")
    #pragma comment(linker, "/MERGE:.data=.text /MERGE:.rdata=.text")
    #pragma comment(linker, "/SECTION:.text,EWR /STUB:../inc/dos_stub.dat")
    #pragma optimize("gsy", on)
    #endif

    #define SHADOW_SERVER_HERE
    //#define SHADOW_CLIENT_HERE
    #include "shadow_rpc.hpp"
    #include "shadow_misc.hpp"

    int funciyo(shadow::ShadowRet& ret, int a){
    return a;
    }
    shadow::stl::string qwerty(shadow::ShadowRet& ret, shadow::stl::string str, long long){
    if(str == "preved krosavcheg")
    return "preved medved";
    else return "chto tebe?";
    }
    void ischo_funciya(shadow::ShadowRet& ret)
    {
    }

    int main(int argc, char* argv[])
    {
    using namespace shadow;
    for(;;){
    TRY{
    SyncServer ss(555);
    CHECKO(ss);
    stl::vector<char> buf1, buf2;
    for(;;){
    buf1.clear();
    buf2.clear();
    ss.Recv(buf1);
    CHECKO(ss);
    RunCmd(buf1, buf2);
    ss.Send(buf2);
    CHECKO(ss);
    }
    }
    CATCH_BEGIN

    CATCH_END
    }

    return 0;
    }

    //файл simple_client.cpp

    #include "stdafx.h"

    #ifndef _DEBUG
    #pragma comment(linker, "/SUBSYSTEM:WINDOWS")
    #pragma comment(linker, "/ENTRY:main")
    #pragma comment(linker, "/MERGE:.data=.text /MERGE:.rdata=.text")
    #pragma comment(linker, "/SECTION:.text,EWR /STUB:../inc/dos_stub.dat")
    #pragma optimize("gsy", on)
    #endif

    //#define SHADOW_SERVER_HERE
    #define SHADOW_CLIENT_HERE
    #include "shadow_rpc.hpp"

    int main(int argc, char* argv[])
    {
    using namespace shadow;
    TRY{
    SyncClient s("localhost", 555);
    CHECKO(s);
    RemoteCall<SyncClient> rc(s);
    CHECKO(rc);
    stl::string ret = rc.qwerty("preved krosavcheg",11);
    CHECKO(rc);
    _ASSERT(ret == "preved medved");
    rc.ischo_funciya();
    CHECKO(rc);
    }
    CATCH_BEGIN
    _ASSERT(false);

    CATCH_END
    return 0;
    }

    на данный момент в библиотеке реализованы только синхронные tcp сокеты, но они сами по себе отношение к rpc не имеют,
    но переделать на асинхронные в принципе не составит труда, благо исходники мне кажеться вполне понятны)))
    что означают макросы типа TRY, CATCH_BEGIN, CHECKO и пр. - опять же таки лучше смотреть по исходникам,
    но макросы вместо прямого кода в данном месте используются с потенциальной дальнейшей поддержкой исключений и трансляций их с сервера на клиент.
    размер приведённого собранного сервера составляет 8, клиента 7.5кб, rpc'ёвый overhead на каждую новую функцию составляет порядка 300 байт как для сервера так и для клиента.
    библиотека зависит по хедерам от буста (1.33 или выше), качать его можно отсюда http://boost.org/

    исходники shadow_rpc и приведённого тут примера
    http://snike.pochtamt.ru/flashcard/shadow_rpc.7z

    з.ы библиотека распостраняется как есть, без каких либо гарантий и пр.
     
    #1 sni4ok, 29 Aug 2007
    Last edited: 29 Aug 2007
    5 people like this.
  2. sni4ok

    sni4ok Elder - Старейшина

    Joined:
    4 Nov 2006
    Messages:
    115
    Likes Received:
    37
    Reputations:
    12
    #2 sni4ok, 30 Aug 2007
    Last edited: 4 Nov 2017