
Здравствуйте, это моя четвёртая статья на хабре, на этот раз я выйду за область 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 необходимо сначала настроить среду разработки
Для начала нужно установить следующие приложения:
Далее продолжаем работу в терминале 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); 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.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.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.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); 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(&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); 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); 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.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.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); 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); 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.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.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.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.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); 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); 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); 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); 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, так-как не претендую на уникальность. Примеры сервисов приведены только, для демонстрации работы конструктора, все протоколы реализованы самостоятельно.
