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