Конструктор клиент-серверных протоколов

от автора

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

В процессе работы над различными проектами, рано или поздно возникает необходимость реализации клиент/серверных протоколов передачи данных, это может быть один из стандартных протоколов типа HTTP, так и реализация своих собственных. Реализация как клиента, так и сервера, всегда занимает большое количество времени, не говоря уже об отладке и вся работа сводится к поиску готовых решений, таких как CURL или NGINX. Когда же требуется реализовать работу своего собственного протокола передачи данных, начинаются проблемы. В один из таких моментов мне пришла в голову идея реализации конструктора, который служил бы каркасом основного приложения и всю работу с сетью брал на себя, чтобы оставалось реализовать только сам протокол. Для этих целей мною был разработан проект AWH.

На данный момент на AWH в качестве демонстрации работоспособности, реализованы следующие примеры:

  • DNS резолвер

  • HTTP(S) клиент

  • WEB сервер

  • Socks5 прокси-сервер

  • HTTPS прокси-сервер

  • WebSocket клиент

  • WebSocket сервер

Поддержка алгоритмов сжатия:

Поддержка методов авторизации:

Поддержка операционных систем:

  • Windows (TCP, TLS, UDP, DTLS)

  • Linux (TCP, TLS, UDP, DTLS, SCTP, UnixSocket)

  • FreeBSD (TCP, TLS, UDP, DTLS, SCTP, UnixSocket)

  • MacOS X (TCP, TLS, UDP, DTLS, UnixSocket)

Установка и сборка:

Для сборки AWH необходимо сначала собрать внешние зависимости. Сам проект базируется на LibEv для *.Nix подобных операционных систем и на LibEvent2 для Windows.

FreeBSD
# Активация модуля ядра SCTP $ sudo kldload sctp  # Клонирование проекта $ git clone --recursive https://github.com/anyks/awh.git  # Переход в каталог проекта $ cd awh  # Сборка зависимостей с поддержкой IDN2 $ ./build_third_party.sh --idn  # Создание каталога сборки проекта $ mkdir ./build  # Переход в каталог сборки проекта $ cd ./build  # Конфигурация проекта с поддержкой модуля IDN2 и Релиз $ cmake \  -DCMAKE_BUILD_IDN=yes \  -DCMAKE_BUILD_TYPE=Release \  ..  # Компиляция проекта $ make  # Генерация SSL ключей для проверки работы DTLS $ cd ./ca $ ./cert.sh

В каталоге «build» будет собрана статическая версия библиотеки libawh.a

Linux (Ubuntu)
# Активация модуля ядра SCTP $ sudo apt install libsctp-dev $ modprobe sctp $ sysctl -w net.sctp.auth_enable=1  # Клонирование проекта $ git clone --recursive https://github.com/anyks/awh.git  # Переход в каталог проекта $ cd awh  # Сборка зависимостей с поддержкой IDN2 $ ./build_third_party.sh --idn  # Создание каталога сборки проекта $ mkdir ./build  # Переход в каталог сборки проекта $ cd ./build  # Конфигурация проекта с поддержкой модуля IDN2 и Релиз $ cmake \  -DCMAKE_BUILD_IDN=yes \  -DCMAKE_BUILD_TYPE=Release \  ..  # Компиляция проекта $ make  # Генерация SSL ключей для проверки работы DTLS $ cd ./ca $ ./cert.sh

В каталоге «build» будет собрана статическая версия библиотеки libawh.a

MacOS X
# Клонирование проекта $ git clone --recursive https://github.com/anyks/awh.git  # Переход в каталог проекта $ cd awh  # Сборка зависимостей с поддержкой IDN2 $ ./build_third_party.sh --idn  # Создание каталога сборки проекта $ mkdir ./build  # Переход в каталог сборки проекта $ cd ./build  # Конфигурация проекта с поддержкой модуля IDN2 и Релиз $ cmake \  -DCMAKE_BUILD_IDN=yes \  -DCMAKE_BUILD_TYPE=Release \  ..  # Компиляция проекта $ make  # Генерация SSL ключей для проверки работы DTLS $ cd ./ca $ ./cert.sh

В каталоге «build» будет собрана статическая версия библиотеки libawh.a

Windows

Для сборки AWH под операционную систему Windows необходимо сначала настроить среду разработки

Для начала нужно установить следующие приложения:

  1. GIT

  2. Perl

  3. Python

  4. MSYS2

  5. CMAKE

Далее продолжаем работу в терминале MSYS2 MinGW64

# Устанавливаем все необходимые нам зависимости $ pacman -Syuu $ pacman -S mingw64/mingw-w64-x86_64-cmake $ pacman -S make $ pacman -S curl $ pacman -S wget $ pacman -S mc $ pacman -S gdb $ pacman -S bash $ pacman -S clang $ pacman -S git $ pacman -S --needed base-devel mingw-w64-x86_64-toolchain $ pacman -S mingw-w64-x86_64-dlfcn
# Клонирование проекта $ git clone --recursive https://github.com/anyks/awh.git  # Переход в каталог проекта $ cd awh  # Сборка зависимостей с поддержкой LibEvent2 $ ./build_third_party.sh --event2  # Создание каталога сборки проекта $ mkdir ./build  # Переход в каталог сборки проекта $ cd ./build  # Конфигурация проекта с поддержкой модуля IDN2 и Релиз $ cmake \  -G "MinGW Makefiles" \  -DCMAKE_BUILD_IDN=yes \  -DCMAKE_BUILD_EVENT2=yes \  -DCMAKE_BUILD_TYPE=Release \  -DCMAKE_SYSTEM_NAME=Windows \  ..  # Компиляция проекта $ cmake --build .

В каталоге «build» будет собрана статическая версия библиотеки libawh.a

Примеры использования библиотеки:

HTTP(S) клиент
#include <client/rest.hpp>  using namespace std; using namespace awh;  class WebClient { private: log_t * _log; public: void active(const client::rest_t::mode_t mode, client::rest_t * web){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == client::rest_t::mode_t::CONNECT ? "Connect" : "Disconnect")); } public: WebClient(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk); network_t nwk(&fmk); uri_t uri(&fmk, &nwk);  WebClient executor(&log);  client::core_t core(&fmk, &log); client::rest_t rest(&core, &fmk, &log);  log.setLogName("REST Client"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  core.ca("./ca/cert.pem"); // core.verifySSL(false); core.sonet(awh::scheme_t::sonet_t::TCP); core.affiliation(awh::core_t::affiliation_t::PRIMARY);  rest.mode( (uint8_t) client::rest_t::flag_t::NOINFO | (uint8_t) client::rest_t::flag_t::WAITMESS | (uint8_t) client::rest_t::flag_t::REDIRECTS | (uint8_t) client::rest_t::flag_t::VERIFYSSL ); // rest.proxy("http://user:password@host.com:port"); rest.proxy("socks5://user:password@host.com:port"); rest.compress(http_t::compress_t::ALL_COMPRESS); rest.on(bind(&WebClient::active, &executor, _1, _2));  const auto & body = rest.GET(uri.parse("https://2ip.ru"), {{"User-Agent", "curl/7.64.1"}});  log.print("ip: %s", log_t::flag_t::INFO, body.data());  return 0; }

Web сервер
#include <server/rest.hpp>  using namespace std; using namespace awh;  class WebServer { private: log_t * _log; public: string password(const string & login){ this->_log->print("USER: %s, PASS: %s", log_t::flag_t::INFO, login.c_str(), "password"); return "password"; } bool auth(const string & login, const string & password){ this->_log->print("USER: %s, PASS: %s", log_t::flag_t::INFO, login.c_str(), password.c_str()); return true; } public: bool accept(const string & ip, const string & mac, const u_int port, server::rest_t * web){ this->_log->print("ACCEPT: ip = %s, mac = %s, port = %d", log_t::flag_t::INFO, ip.c_str(), mac.c_str(), port); return true; } void active(const size_t aid, const server::rest_t::mode_t mode, server::rest_t * web){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == server::rest_t::mode_t::CONNECT ? "Connect" : "Disconnect")); } void message(const size_t aid, const awh::http_t * http, server::rest_t * web){ const auto & query = http->query();  if(!query.uri.empty() && (query.uri.find("favicon.ico") != string::npos)) web->reject(aid, 404); else if(query.method == web_t::method_t::GET){ const string body = "<html>\n<head>\n<title>Hello World!</title>\n</head>\n<body>\n" "<h1>\"Hello, World!\" program</h1>\n" "<div>\nFrom Wikipedia, the free encyclopedia<br>\n" "(Redirected from Hello, world!)<br>\n" "Jump to navigationJump to search<br>\n" "<strong>\"Hello World\"</strong> redirects here. For other uses, see Hello World (disambiguation).<br>\n" "A <strong>\"Hello, World!\"</strong> program generally is a computer program that outputs or displays the message \"Hello, World!\".<br>\n" "Such a program is very simple in most programming languages, and is often used to illustrate the basic syntax of a programming language. It is often the first program written by people learning to code. It can also be used as a sanity test to make sure that computer software intended to compile or run source code is correctly installed, and that the operator understands how to use it.\n" "</div>\n</body>\n</html>\n"; web->response(aid, 200, "OK", vector <char> (body.begin(), body.end())); } } public: WebServer(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  WebServer executor(&log);  server::core_t core(&fmk, &log); server::rest_t rest(&core, &fmk, &log);  log.setLogName("Web Server"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  core.clusterSize(4); core.verifySSL(false); core.sonet(awh::scheme_t::sonet_t::TLS); core.affiliation(awh::core_t::affiliation_t::PRIMARY); core.certificate("./ca/certs/server-cert.pem", "./ca/certs/server-key.pem");  rest.realm("ANYKS"); rest.opaque("keySession"); rest.authType(auth_t::type_t::DIGEST, auth_t::hash_t::MD5); rest.init(2222, "127.0.0.1", http_t::compress_t::ALL_COMPRESS);  rest.on((function <string (const string &)>) bind(&WebServer::password, &executor, _1)); // rest.on((function <bool (const string &, const string &)>) bind(&WebServer::auth, &executor, _1, _2)); rest.on((function <void (const size_t, const awh::http_t *, server::rest_t *)>) bind(&WebServer::message, &executor, _1, _2, _3)); rest.on((function <void (const size_t, const server::rest_t::mode_t, server::rest_t *)>) bind(&WebServer::active, &executor, _1, _2, _3)); rest.on((function <bool (const string &, const string &, const u_int, server::rest_t *)>) bind(&WebServer::accept, &executor, _1, _2, _3, _4));  rest.start();  return 0; }

WebSocket клиент
#include <client/ws.hpp>  using namespace std; using namespace awh;  class WebSocket { private: log_t * _log; public: void active(const client::ws_t::mode_t mode, client::ws_t * ws){ this->_log->print("%s server", log_t::flag_t::INFO, (mode == client::ws_t::mode_t::CONNECT ? "Start" : "Stop"));  if(mode == client::ws_t::mode_t::CONNECT){ const string query = "{\"text\":\"Hello World!\"}"; ws->send(query.data(), query.size()); } } void error(const u_int code, const string & mess, client::ws_t * ws){ this->_log->print("%s [%u]", log_t::flag_t::CRITICAL, mess.c_str(), code); } void message(const vector <char> & buffer, const bool utf8, client::ws_t * ws){ if(utf8 && !buffer.empty()) this->_log->print("message: %s [%s]", log_t::flag_t::INFO, string(buffer.begin(), buffer.end()).c_str(), ws->sub().c_str()); } public: WebSocket(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  WebSocket executor(&log);  client::core_t core(&fmk, &log); client::ws_t ws(&core, &fmk, &log);  log.setLogName("WebSocket Client"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  ws.mode( (uint8_t) client::ws_t::flag_t::TAKEOVERCLI | (uint8_t) client::ws_t::flag_t::TAKEOVERSRV | (uint8_t) client::ws_t::flag_t::VERIFYSSL | (uint8_t) client::ws_t::flag_t::KEEPALIVE );  core.verifySSL(false); core.ca("./ca/cert.pem"); core.sonet(awh::scheme_t::sonet_t::TLS); core.affiliation(awh::core_t::affiliation_t::PRIMARY); core.certificate("./ca/certs/client-cert.pem", "./ca/certs/client-key.pem");  // ws.proxy("http://user:password@host.com:port"); // ws.proxy("socks5://user:password@host.com:port"); // ws.authTypeProxy(auth_t::type_t::BASIC); // ws.authTypeProxy(auth_t::type_t::DIGEST, auth_t::hash_t::MD5);  ws.user("user", "password"); // ws.authType(auth_t::type_t::BASIC); ws.authType(auth_t::type_t::DIGEST, auth_t::hash_t::MD5);  ws.subs({"test2", "test8", "test9"}); ws.init("wss://127.0.0.1:2222", http_t::compress_t::DEFLATE);  ws.on(bind(&WebSocket::active, &executor, _1, _2)); ws.on(bind(&WebSocket::error, &executor, _1, _2, _3)); ws.on(bind(&WebSocket::message, &executor, _1, _2, _3));  ws.start();  return 0; }

WebSocket сервер
#include <server/ws.hpp>  using namespace std; using namespace awh;  class WebSocket { private: log_t * _log; public: string password(const string & login){ this->_log->print("USER: %s, PASS: %s", log_t::flag_t::INFO, login.c_str(), "password"); return "password"; } bool auth(const string & login, const string & password){ this->_log->print("USER: %s, PASS: %s", log_t::flag_t::INFO, login.c_str(), password.c_str()); return true; } public: bool accept(const string & ip, const string & mac, const u_int port, server::ws_t * ws){ this->_log->print("ACCEPT: ip = %s, mac = %s, port = %d", log_t::flag_t::INFO, ip.c_str(), mac.c_str(), port); return true; } void active(const size_t aid, const server::ws_t::mode_t mode, server::ws_t * ws){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == server::ws_t::mode_t::CONNECT ? "Connect" : "Disconnect")); } void error(const size_t aid, const u_int code, const string & mess, server::ws_t * ws){ this->_log->print("%s [%u]", log_t::flag_t::CRITICAL, mess.c_str(), code); } void message(const size_t aid, const vector <char> & buffer, const bool utf8, server::ws_t * ws){ if(!buffer.empty()){ this->_log->print("message: %s [%s]", log_t::flag_t::INFO, string(buffer.begin(), buffer.end()).c_str(), ws->sub(aid).c_str()); ws->send(aid, buffer.data(), buffer.size(), utf8); } } public: WebSocket(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  WebSocket executor(&log);  server::core_t core(&fmk, &log); server::ws_t ws(&core, &fmk, &log);  log.setLogName("WebSocket Server"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  core.clusterSize(); core.verifySSL(false); core.sonet(awh::scheme_t::sonet_t::TLS); core.affiliation(awh::core_t::affiliation_t::PRIMARY); core.certificate("./ca/certs/server-cert.pem", "./ca/certs/server-key.pem");  ws.realm("ANYKS"); ws.opaque("keySession"); ws.subs({"test1", "test2", "test3"});  // ws.authType(auth_t::type_t::BASIC); ws.authType(auth_t::type_t::DIGEST, auth_t::hash_t::MD5); ws.init(2222, "127.0.0.1", http_t::compress_t::DEFLATE);  ws.on((function <string (const string &)>) bind(&WebSocket::password, &executor, _1)); // ws.on((function <bool (const string &, const string &)>) bind(&WebSocket::auth, &executor, _1, _2)); ws.on((function <void (const size_t, const server::ws_t::mode_t, server::ws_t *)>) bind(&WebSocket::active, &executor, _1, _2, _3)); ws.on((function <void (const size_t, const u_int, const string &, server::ws_t *)>) bind(&WebSocket::error, &executor, _1, _2, _3, _4)); ws.on((function <bool (const string &, const string &, const u_int, server::ws_t *)>) bind(&WebSocket::accept, &executor, _1, _2, _3, _4)); ws.on((function <void (const size_t, const vector <char> &, const bool, server::ws_t *)>) bind(&WebSocket::message, &executor, _1, _2, _3, _4));  ws.start();  return 0; }

HTTPS прокси-сервер
#include <server/proxy.hpp>  using namespace std; using namespace awh;  class Proxy { private: log_t * _log; public: string password(const string & login){ this->_log->print("USER: %s, PASS: %s", log_t::flag_t::INFO, login.c_str(), "password"); return "password"; } bool auth(const string & login, const string & password){ this->_log->print("USER: %s, PASS: %s", log_t::flag_t::INFO, login.c_str(), password.c_str()); return true; } public: bool accept(const string & ip, const string & mac, const u_int port, server::proxy_t * proxy){ this->_log->print("ACCEPT: ip = %s, mac = %s, port = %d", log_t::flag_t::INFO, ip.c_str(), mac.c_str(), port); return true; } void active(const size_t aid, const server::proxy_t::mode_t mode, server::proxy_t * proxy){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == server::proxy_t::mode_t::CONNECT ? "Connect" : "Disconnect")); } bool message(const size_t aid, const server::proxy_t::event_t event, awh::http_t * http, server::proxy_t * proxy){ cout << (event == server::proxy_t::event_t::REQUEST ? "REQUEST" : "RESPONSE") << endl; for(auto & header : http->headers()) cout << "Header: " << header.first << " = " << header.second << endl << endl; return true; } public: Proxy(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Proxy executor(&log);  server::proxy_t proxy(&fmk, &log);  log.setLogName("Proxy Server"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  proxy.mode( (uint8_t) server::proxy_t::flag_t::NOINFO | (uint8_t) server::proxy_t::flag_t::WAITMESS ); proxy.clusterSize();  // proxy.realm("ANYKS"); // proxy.opaque("keySession");  proxy.authType(auth_t::type_t::BASIC); // proxy.authType(auth_t::type_t::DIGEST, auth_t::hash_t::MD5); proxy.sonet(awh::scheme_t::sonet_t::TCP); proxy.init(2222, "127.0.0.1", http_t::compress_t::GZIP);  // proxy.on((function <string (const string &)>) bind(&Proxy::password, &executor, _1)); proxy.on((function <bool (const string &, const string &)>) bind(&Proxy::auth, &executor, _1, _2)); proxy.on((function <void (const size_t, const server::proxy_t::mode_t, server::proxy_t *)>) bind(&Proxy::active, &executor, _1, _2, _3)); proxy.on((function <bool (const string &, const string &, const u_int, server::proxy_t *)>) bind(&Proxy::accept, &executor, _1, _2, _3, _4)); proxy.on((function <bool (const size_t, const server::proxy_t::event_t, awh::http_t *, server::proxy_t *)>) bind(&Proxy::message, &executor, _1, _2, _3, _4));  proxy.start();  return 0; }

Socks5 прокси-сервер
#include <server/socks5.hpp>  using namespace std; using namespace awh; using namespace server;  class Proxy { private: log_t * _log; public: bool auth(const string & login, const string & password){ this->_log->print("USER: %s, PASS: %s", log_t::flag_t::INFO, login.c_str(), password.c_str()); return true; } public: bool accept(const string & ip, const string & mac, const u_int port, proxy_socks5_t * proxy){ this->_log->print("ACCEPT: ip = %s, mac = %s, port = %d", log_t::flag_t::INFO, ip.c_str(), mac.c_str(), port); return true; } void active(const size_t aid, const proxy_socks5_t::mode_t mode, proxy_socks5_t * proxy){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == proxy_socks5_t::mode_t::CONNECT ? "Connect" : "Disconnect")); } public: Proxy(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Proxy executor(&log);  proxy_socks5_t proxy(&fmk, &log);  log.setLogName("Proxy Socks5 Server"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  proxy.mode( (uint8_t) proxy_socks5_t::flag_t::NOINFO | (uint8_t) proxy_socks5_t::flag_t::WAITMESS ); proxy.clusterSize();  proxy.init(2222, "127.0.0.1");  proxy.on((function <bool (const string &, const string &)>) bind(&Proxy::auth, &executor, _1, _2)); proxy.on((function <void (const size_t, const proxy_socks5_t::mode_t, proxy_socks5_t *)>) bind(&Proxy::active, &executor, _1, _2, _3)); proxy.on((function <bool (const string &, const string &, const u_int, proxy_socks5_t *)>) bind(&Proxy::accept, &executor, _1, _2, _3, _4));  proxy.start();  return 0; }

Таймер
#include <chrono> #include <core/core.hpp>  using namespace std; using namespace awh;  class Timer { private: chrono::time_point <chrono::system_clock> ts; chrono::time_point <chrono::system_clock> is; private: u_short count; private: log_t * _log; public: void interval(const u_short id, core_t * core){ auto shift = chrono::system_clock::now();  this->_log->print("Interval: %u seconds", log_t::flag_t::INFO, chrono::duration_cast <chrono::seconds> (shift - this->is).count());  this->is = shift;  if((this->count++) >= 10){ core->clearTimer(id); core->stop(); } } void timeout(const u_short id, core_t * core){ this->_log->print("Timeout: %u seconds", log_t::flag_t::INFO, chrono::duration_cast <chrono::seconds> (chrono::system_clock::now() - this->ts).count()); } void run(const bool mode, Core * core){ if(mode){ this->ts = chrono::system_clock::now(); this->is = chrono::system_clock::now();  this->_log->print("%s", log_t::flag_t::INFO, "Start timer");  core->setTimeout(10000, (function <void (const u_short, core_t *)>) bind(&Timer::timeout, this, _1, _2)); core->setInterval(5000, (function <void (const u_short, core_t *)>) bind(&Timer::interval, this, _1, _2)); } else this->_log->print("%s", log_t::flag_t::INFO, "Stop timer"); } public: Timer(log_t * log) : ts(chrono::system_clock::now()), is(chrono::system_clock::now()), count(0), _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Timer executor(&log);  core_t core(awh::core_t::affiliation_t::PRIMARY, &fmk, &log);  log.setLogName("Timer"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  core.callback((function <void (const bool, core_t *)>) bind(&Timer::run, &executor, _1, _2));  core.start();  return 0; }

DNS резолвер
#include <core/core.hpp>  using namespace std; using namespace awh;  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk); core_t core(awh::core_t::affiliation_t::PRIMARY, &fmk, &log);  log.setLogName("DNS"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  core.resolve("google.com", scheme_t::family_t::IPV4, [&log](const string & ip, const scheme_t::family_t family, core_t * core){ log.print("IP: %s", log_t::flag_t::INFO, ip.c_str()); core->stop(); });  core.start();  return 0; }

TCP клиент
#include <client/sample.hpp>  using namespace std; using namespace awh;  class Client { private: log_t * _log; public: void active(const client::sample_t::mode_t mode, client::sample_t * sample){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == client::sample_t::mode_t::CONNECT ? "Connect" : "Disconnect")); if(mode == client::sample_t::mode_t::CONNECT){ const string message = "Hello World!!!"; sample->send(message.data(), message.size()); } } void message(const vector <char> & buffer, client::sample_t * sample){ const string message(buffer.begin(), buffer.end()); this->_log->print("%s", log_t::flag_t::INFO, message.c_str()); sample->stop(); } public: Client(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Client executor(&log);  client::core_t core(&fmk, &log); client::sample_t sample(&core, &fmk, &log);  log.setLogName("SAMPLE Client"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  sample.mode( // (uint8_t) client::sample_t::flag_t::NOINFO | (uint8_t) client::sample_t::flag_t::WAITMESS | (uint8_t) client::sample_t::flag_t::VERIFYSSL ); core.sonet(awh::scheme_t::sonet_t::TCP); core.affiliation(awh::core_t::affiliation_t::PRIMARY);  sample.init(2222, "127.0.0.1"); sample.on(bind(&Client::active, &executor, _1, _2)); sample.on(bind(&Client::message, &executor, _1, _2));  sample.start();  return 0; }

TCP сервер
#include <server/sample.hpp>  using namespace std; using namespace awh;  class Server { private: log_t * _log; public: bool accept(const string & ip, const string & mac, const u_int port, server::sample_t * sample){ this->_log->print("ACCEPT: ip = %s, mac = %s, port = %d", log_t::flag_t::INFO, ip.c_str(), mac.c_str(), port); return true; } void active(const size_t aid, const server::sample_t::mode_t mode, server::sample_t * sample){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == server::sample_t::mode_t::CONNECT ? "Connect" : "Disconnect")); } void message(const size_t aid, const vector <char> & buffer, server::sample_t * sample){ const string message(buffer.begin(), buffer.end()); this->_log->print("%s", log_t::flag_t::INFO, message.c_str()); sample->send(aid, buffer.data(), buffer.size()); } public: Server(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Server executor(&log);  server::core_t core(&fmk, &log); server::sample_t sample(&core, &fmk, &log);  log.setLogName("SAMPLE Server"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  core.sonet(awh::scheme_t::sonet_t::TCP); core.affiliation(awh::core_t::affiliation_t::PRIMARY);  sample.init(2222, "127.0.0.1"); sample.on((function <void (const size_t, const vector <char> &, server::sample_t *)>) bind(&Server::message, &executor, _1, _2, _3)); sample.on((function <void (const size_t, const server::sample_t::mode_t, server::sample_t *)>) bind(&Server::active, &executor, _1, _2, _3)); sample.on((function <bool (const string &, const string &, const u_int, server::sample_t *)>) bind(&Server::accept, &executor, _1, _2, _3, _4));  sample.start();  return 0; }

TLS клиент
#include <client/sample.hpp>  using namespace std; using namespace awh;  class Client { private: log_t * _log; public: void active(const client::sample_t::mode_t mode, client::sample_t * sample){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == client::sample_t::mode_t::CONNECT ? "Connect" : "Disconnect")); if(mode == client::sample_t::mode_t::CONNECT){ const string message = "Hello World!!!"; sample->send(message.data(), message.size()); } } void message(const vector <char> & buffer, client::sample_t * sample){ const string message(buffer.begin(), buffer.end()); this->_log->print("%s", log_t::flag_t::INFO, message.c_str()); sample->stop(); } public: Client(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Client executor(&log);  client::core_t core(&fmk, &log); client::sample_t sample(&core, &fmk, &log);  log.setLogName("SAMPLE Client"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  sample.mode( // (uint8_t) client::sample_t::flag_t::NOINFO | (uint8_t) client::sample_t::flag_t::WAITMESS | (uint8_t) client::sample_t::flag_t::VERIFYSSL ); core.verifySSL(false); core.ca("./ca/cert.pem"); core.sonet(awh::scheme_t::sonet_t::TLS); core.affiliation(awh::core_t::affiliation_t::PRIMARY); core.certificate("./ca/certs/client-cert.pem", "./ca/certs/client-key.pem");  sample.init(2222, "127.0.0.1"); sample.on(bind(&Client::active, &executor, _1, _2)); sample.on(bind(&Client::message, &executor, _1, _2));  sample.start();  return 0; }

TLS сервер
#include <server/sample.hpp>  using namespace std; using namespace awh;  class Server { private: log_t * _log; public: bool accept(const string & ip, const string & mac, const u_int port, server::sample_t * sample){ this->_log->print("ACCEPT: ip = %s, mac = %s, port = %d", log_t::flag_t::INFO, ip.c_str(), mac.c_str(), port); return true; } void active(const size_t aid, const server::sample_t::mode_t mode, server::sample_t * sample){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == server::sample_t::mode_t::CONNECT ? "Connect" : "Disconnect")); } void message(const size_t aid, const vector <char> & buffer, server::sample_t * sample){ const string message(buffer.begin(), buffer.end()); this->_log->print("%s", log_t::flag_t::INFO, message.c_str()); sample->send(aid, buffer.data(), buffer.size()); } public: Server(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Server executor(&log);  server::core_t core(&fmk, &log); server::sample_t sample(&core, &fmk, &log);  log.setLogName("SAMPLE Server"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  core.verifySSL(false); core.sonet(awh::scheme_t::sonet_t::TLS); core.affiliation(awh::core_t::affiliation_t::PRIMARY); core.certificate("./ca/certs/server-cert.pem", "./ca/certs/server-key.pem");  sample.init(2222, "127.0.0.1"); sample.on((function <void (const size_t, const vector <char> &, server::sample_t *)>) bind(&Server::message, &executor, _1, _2, _3)); sample.on((function <void (const size_t, const server::sample_t::mode_t, server::sample_t *)>) bind(&Server::active, &executor, _1, _2, _3)); sample.on((function <bool (const string &, const string &, const u_int, server::sample_t *)>) bind(&Server::accept, &executor, _1, _2, _3, _4));  sample.start();  return 0; }

UDP клиент
#include <client/sample.hpp>  using namespace std; using namespace awh;  class Client { private: log_t * _log; public: void active(const client::sample_t::mode_t mode, client::sample_t * sample){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == client::sample_t::mode_t::CONNECT ? "Connect" : "Disconnect")); if(mode == client::sample_t::mode_t::CONNECT){ const string message = "Hello World!!!"; sample->send(message.data(), message.size()); } } void message(const vector <char> & buffer, client::sample_t * sample){ const string message(buffer.begin(), buffer.end()); this->_log->print("%s", log_t::flag_t::INFO, message.c_str()); sample->stop(); } public: Client(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Client executor(&log);  client::core_t core(&fmk, &log); client::sample_t sample(&core, &fmk, &log);  log.setLogName("SAMPLE Client"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  sample.mode( // (uint8_t) client::sample_t::flag_t::NOINFO | (uint8_t) client::sample_t::flag_t::WAITMESS | (uint8_t) client::sample_t::flag_t::VERIFYSSL ); core.sonet(awh::scheme_t::sonet_t::UDP); core.affiliation(awh::core_t::affiliation_t::PRIMARY);  sample.init(2222, "127.0.0.1"); sample.on(bind(&Client::active, &executor, _1, _2)); sample.on(bind(&Client::message, &executor, _1, _2));  sample.start();  return 0; }

UDP сервер
#include <server/sample.hpp>  using namespace std; using namespace awh;  class Server { private: log_t * _log; public: bool accept(const string & ip, const string & mac, const u_int port, server::sample_t * sample){ this->_log->print("ACCEPT: ip = %s, mac = %s, port = %d", log_t::flag_t::INFO, ip.c_str(), mac.c_str(), port); return true; } void active(const size_t aid, const server::sample_t::mode_t mode, server::sample_t * sample){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == server::sample_t::mode_t::CONNECT ? "Connect" : "Disconnect")); } void message(const size_t aid, const vector <char> & buffer, server::sample_t * sample){ const string message(buffer.begin(), buffer.end()); this->_log->print("%s", log_t::flag_t::INFO, message.c_str()); sample->send(aid, buffer.data(), buffer.size()); } public: Server(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Server executor(&log);  server::core_t core(&fmk, &log); server::sample_t sample(&core, &fmk, &log);  log.setLogName("SAMPLE Server"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  core.sonet(awh::scheme_t::sonet_t::UDP); core.affiliation(awh::core_t::affiliation_t::PRIMARY);  sample.init(2222, "127.0.0.1"); sample.on((function <void (const size_t, const vector <char> &, server::sample_t *)>) bind(&Server::message, &executor, _1, _2, _3)); sample.on((function <void (const size_t, const server::sample_t::mode_t, server::sample_t *)>) bind(&Server::active, &executor, _1, _2, _3)); sample.on((function <bool (const string &, const string &, const u_int, server::sample_t *)>) bind(&Server::accept, &executor, _1, _2, _3, _4));  sample.start();  return 0; }

SCTP клиент
#include <client/sample.hpp>  using namespace std; using namespace awh;  class Client { private: log_t * _log; public: void active(const client::sample_t::mode_t mode, client::sample_t * sample){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == client::sample_t::mode_t::CONNECT ? "Connect" : "Disconnect")); if(mode == client::sample_t::mode_t::CONNECT){ const string message = "Hello World!!!"; sample->send(message.data(), message.size()); } } void message(const vector <char> & buffer, client::sample_t * sample){ const string message(buffer.begin(), buffer.end()); this->_log->print("%s", log_t::flag_t::INFO, message.c_str()); sample->stop(); } public: Client(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Client executor(&log);  client::core_t core(&fmk, &log); client::sample_t sample(&core, &fmk, &log);  log.setLogName("SAMPLE Client"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  sample.mode( // (uint8_t) client::sample_t::flag_t::NOINFO | (uint8_t) client::sample_t::flag_t::WAITMESS | (uint8_t) client::sample_t::flag_t::VERIFYSSL ); core.verifySSL(false); core.ca("./ca/cert.pem"); core.sonet(awh::scheme_t::sonet_t::SCTP); core.affiliation(awh::core_t::affiliation_t::PRIMARY); core.certificate("./ca/certs/client-cert.pem", "./ca/certs/client-key.pem");  sample.init(2222, "127.0.0.1"); sample.on(bind(&Client::active, &executor, _1, _2)); sample.on(bind(&Client::message, &executor, _1, _2));  sample.start();  return 0; }

SCTP сервер
#include <server/sample.hpp>  using namespace std; using namespace awh;  class Server { private: log_t * _log; public: bool accept(const string & ip, const string & mac, const u_int port, server::sample_t * sample){ this->_log->print("ACCEPT: ip = %s, mac = %s, port = %d", log_t::flag_t::INFO, ip.c_str(), mac.c_str(), port); return true; } void active(const size_t aid, const server::sample_t::mode_t mode, server::sample_t * sample){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == server::sample_t::mode_t::CONNECT ? "Connect" : "Disconnect")); } void message(const size_t aid, const vector <char> & buffer, server::sample_t * sample){ const string message(buffer.begin(), buffer.end()); this->_log->print("%s", log_t::flag_t::INFO, message.c_str()); sample->send(aid, buffer.data(), buffer.size()); } public: Server(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Server executor(&log);  server::core_t core(&fmk, &log); server::sample_t sample(&core, &fmk, &log);  log.setLogName("SAMPLE Server"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  core.verifySSL(false); core.sonet(awh::scheme_t::sonet_t::SCTP); core.affiliation(awh::core_t::affiliation_t::PRIMARY); core.certificate("./ca/certs/server-cert.pem", "./ca/certs/server-key.pem");  sample.init(2222, "127.0.0.1"); sample.on((function <void (const size_t, const vector <char> &, server::sample_t *)>) bind(&Server::message, &executor, _1, _2, _3)); sample.on((function <void (const size_t, const server::sample_t::mode_t, server::sample_t *)>) bind(&Server::active, &executor, _1, _2, _3)); sample.on((function <bool (const string &, const string &, const u_int, server::sample_t *)>) bind(&Server::accept, &executor, _1, _2, _3, _4));  sample.start();  return 0; }

DTLS клиент
#include <client/sample.hpp>  using namespace std; using namespace awh;  class Client { private: log_t * _log; public: void active(const client::sample_t::mode_t mode, client::sample_t * sample){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == client::sample_t::mode_t::CONNECT ? "Connect" : "Disconnect")); if(mode == client::sample_t::mode_t::CONNECT){ const string message = "Hello World!!!"; sample->send(message.data(), message.size()); } } void message(const vector <char> & buffer, client::sample_t * sample){ const string message(buffer.begin(), buffer.end()); this->_log->print("%s", log_t::flag_t::INFO, message.c_str()); sample->stop(); } public: Client(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Client executor(&log);  client::core_t core(&fmk, &log); client::sample_t sample(&core, &fmk, &log);  log.setLogName("SAMPLE Client"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  sample.mode( // (uint8_t) client::sample_t::flag_t::NOINFO | (uint8_t) client::sample_t::flag_t::WAITMESS | (uint8_t) client::sample_t::flag_t::VERIFYSSL ); core.verifySSL(false); core.ca("./ca/cert.pem"); core.sonet(awh::scheme_t::sonet_t::DTLS); core.affiliation(awh::core_t::affiliation_t::PRIMARY); core.certificate("./ca/certs/client-cert.pem", "./ca/certs/client-key.pem");  sample.init(2222, "127.0.0.1"); sample.on(bind(&Client::active, &executor, _1, _2)); sample.on(bind(&Client::message, &executor, _1, _2));  sample.start();  return 0; }

DTLS сервер
#include <server/sample.hpp>  using namespace std; using namespace awh;  class Server { private: log_t * _log; public: bool accept(const string & ip, const string & mac, const u_int port, server::sample_t * sample){ this->_log->print("ACCEPT: ip = %s, mac = %s, port = %d", log_t::flag_t::INFO, ip.c_str(), mac.c_str(), port); return true; } void active(const size_t aid, const server::sample_t::mode_t mode, server::sample_t * sample){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == server::sample_t::mode_t::CONNECT ? "Connect" : "Disconnect")); } void message(const size_t aid, const vector <char> & buffer, server::sample_t * sample){ const string message(buffer.begin(), buffer.end()); this->_log->print("%s", log_t::flag_t::INFO, message.c_str()); sample->send(aid, buffer.data(), buffer.size()); } public: Server(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Server executor(&log);  server::core_t core(&fmk, &log); server::sample_t sample(&core, &fmk, &log);  log.setLogName("SAMPLE Server"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  core.verifySSL(false); core.sonet(awh::scheme_t::sonet_t::DTLS); core.affiliation(awh::core_t::affiliation_t::PRIMARY); core.certificate("./ca/certs/server-cert.pem", "./ca/certs/server-key.pem");  sample.init(2222, "127.0.0.1"); sample.on((function <void (const size_t, const vector <char> &, server::sample_t *)>) bind(&Server::message, &executor, _1, _2, _3)); sample.on((function <void (const size_t, const server::sample_t::mode_t, server::sample_t *)>) bind(&Server::active, &executor, _1, _2, _3)); sample.on((function <bool (const string &, const string &, const u_int, server::sample_t *)>) bind(&Server::accept, &executor, _1, _2, _3, _4));  sample.start();  return 0; }

TCP UnixSocket клиент
#include <client/sample.hpp>  using namespace std; using namespace awh;  class Client { private: log_t * _log; public: void active(const client::sample_t::mode_t mode, client::sample_t * sample){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == client::sample_t::mode_t::CONNECT ? "Connect" : "Disconnect")); if(mode == client::sample_t::mode_t::CONNECT){ const string message = "Hello World!!!"; sample->send(message.data(), message.size()); } } void message(const vector <char> & buffer, client::sample_t * sample){ const string message(buffer.begin(), buffer.end()); this->_log->print("%s", log_t::flag_t::INFO, message.c_str()); sample->stop(); } public: Client(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Client executor(&log);  client::core_t core(&fmk, &log); client::sample_t sample(&core, &fmk, &log);  log.setLogName("SAMPLE Client"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  sample.mode( // (uint8_t) client::sample_t::flag_t::NOINFO | (uint8_t) client::sample_t::flag_t::WAITMESS | (uint8_t) client::sample_t::flag_t::VERIFYSSL ); core.sonet(awh::scheme_t::sonet_t::TCP); core.family(awh::scheme_t::family_t::NIX); core.affiliation(awh::core_t::affiliation_t::PRIMARY);  sample.init("anyks"); sample.on(bind(&Client::active, &executor, _1, _2)); sample.on(bind(&Client::message, &executor, _1, _2));  sample.start();  return 0; }

TCP UnixSocket сервер
#include <server/sample.hpp>  using namespace std; using namespace awh;  class Server { private: log_t * _log; public: bool accept(const string & ip, const string & mac, const u_int port, server::sample_t * sample){ this->_log->print("ACCEPT: ip = %s, mac = %s, port = %d", log_t::flag_t::INFO, ip.c_str(), mac.c_str(), port); return true; } void active(const size_t aid, const server::sample_t::mode_t mode, server::sample_t * sample){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == server::sample_t::mode_t::CONNECT ? "Connect" : "Disconnect")); } void message(const size_t aid, const vector <char> & buffer, server::sample_t * sample){ const string message(buffer.begin(), buffer.end()); this->_log->print("%s", log_t::flag_t::INFO, message.c_str()); sample->send(aid, buffer.data(), buffer.size()); } public: Server(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Server executor(&log);  server::core_t core(&fmk, &log); server::sample_t sample(&core, &fmk, &log);  log.setLogName("SAMPLE Server"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  core.sonet(awh::scheme_t::sonet_t::TCP); core.family(awh::scheme_t::family_t::NIX); core.affiliation(awh::core_t::affiliation_t::PRIMARY);  sample.init("anyks"); sample.on((function <void (const size_t, const vector <char> &, server::sample_t *)>) bind(&Server::message, &executor, _1, _2, _3)); sample.on((function <void (const size_t, const server::sample_t::mode_t, server::sample_t *)>) bind(&Server::active, &executor, _1, _2, _3)); sample.on((function <bool (const string &, const string &, const u_int, server::sample_t *)>) bind(&Server::accept, &executor, _1, _2, _3, _4));  sample.start();  return 0; }

UDP UnixSocket клиент
#include <client/sample.hpp>  using namespace std; using namespace awh;  class Client { private: log_t * _log; public: void active(const client::sample_t::mode_t mode, client::sample_t * sample){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == client::sample_t::mode_t::CONNECT ? "Connect" : "Disconnect")); if(mode == client::sample_t::mode_t::CONNECT){ const string message = "Hello World!!!"; sample->send(message.data(), message.size()); } } void message(const vector <char> & buffer, client::sample_t * sample){ const string message(buffer.begin(), buffer.end()); this->_log->print("%s", log_t::flag_t::INFO, message.c_str()); sample->stop(); } public: Client(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Client executor(&log);  client::core_t core(&fmk, &log); client::sample_t sample(&core, &fmk, &log);  log.setLogName("SAMPLE Client"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  sample.mode( // (uint8_t) client::sample_t::flag_t::NOINFO | (uint8_t) client::sample_t::flag_t::WAITMESS | (uint8_t) client::sample_t::flag_t::VERIFYSSL ); core.sonet(awh::scheme_t::sonet_t::UDP); core.family(awh::scheme_t::family_t::NIX); core.affiliation(awh::core_t::affiliation_t::PRIMARY);  sample.init("anyks"); sample.on(bind(&Client::active, &executor, _1, _2)); sample.on(bind(&Client::message, &executor, _1, _2));  sample.start();  return 0; }

UDP UnixSocket сервер
#include <server/sample.hpp>  using namespace std; using namespace awh;  class Server { private: log_t * _log; public: bool accept(const string & ip, const string & mac, const u_int port, server::sample_t * sample){ this->_log->print("ACCEPT: ip = %s, mac = %s, port = %d", log_t::flag_t::INFO, ip.c_str(), mac.c_str(), port); return true; } void active(const size_t aid, const server::sample_t::mode_t mode, server::sample_t * sample){ this->_log->print("%s client", log_t::flag_t::INFO, (mode == server::sample_t::mode_t::CONNECT ? "Connect" : "Disconnect")); } void message(const size_t aid, const vector <char> & buffer, server::sample_t * sample){ const string message(buffer.begin(), buffer.end()); this->_log->print("%s", log_t::flag_t::INFO, message.c_str()); sample->send(aid, buffer.data(), buffer.size()); } public: Server(log_t * log) : _log(log) {} };  int main(int argc, char * argv[]){ fmk_t fmk; log_t log(&fmk);  Server executor(&log);  server::core_t core(&fmk, &log); server::sample_t sample(&core, &fmk, &log);  log.setLogName("SAMPLE Server"); log.setLogFormat("%H:%M:%S %d.%m.%Y");  core.sonet(awh::scheme_t::sonet_t::UDP); core.family(awh::scheme_t::family_t::NIX); core.affiliation(awh::core_t::affiliation_t::PRIMARY);  sample.init("anyks"); sample.on((function <void (const size_t, const vector <char> &, server::sample_t *)>) bind(&Server::message, &executor, _1, _2, _3)); sample.on((function <void (const size_t, const server::sample_t::mode_t, server::sample_t *)>) bind(&Server::active, &executor, _1, _2, _3)); sample.on((function <bool (const string &, const string &, const u_int, server::sample_t *)>) bind(&Server::accept, &executor, _1, _2, _3, _4));  sample.start();  return 0; }

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

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


ссылка на оригинал статьи https://habr.com/ru/post/694146/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *