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

Комментарии (21)


  1. funca
    19.10.2022 09:11
    +2

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

    Мне кажется заголовок "Конструктор клиент-серверных протоколов" вводит в заблуждение. Протокол это что-то формальное и отделенное от реализации. Задизнайнить протокол задачка сама по себе не тривиальная.

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


    1. frazer Автор
      19.10.2022 09:17

      Библиотека разрабатывалась как потокобезопасная, насчёт подводных камней сложный вопрос. Архитектура уже несколько раз полностью перерабатывалась как раз из-за выявления этих подводных камней, не исключено, что глобальные доработки будут ещё. Про конкурентные запросы не понял, вы имеете в виду HTTP запросы?


  1. eoanermine
    19.10.2022 09:23
    +7

    Это определенно не конструктор протоколов.

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


    1. frazer Автор
      19.10.2022 09:30
      -2

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


      1. alan008
        19.10.2022 09:36
        +5

        Скорее всего ваш проект относится к категории middleware фреймворков — это по сути сетевая библиотека, реализующая клиентский и серверный концы. Это не конструктор, это просто абстракция над слоем более низкого уровня.


  1. Apoheliy
    19.10.2022 09:39
    +3

    Поправьте, пожалуйста, но (по-моему) вы не даёте конструировать протокол.

    Вы НАВЯЗЫВАЕТЕ протокол. Один из протоколов. Т.е., например, если у меня есть протокол посылки пакетов с определённым заголовком (об 7 байтах, из них 4 байта длина пакета), то не понятно, как эту логику разделения на пакеты/сообщения внедрить в работу с awh библиотекой.

    Ещё не понятен вопрос многопоточности: как запустить сервер на 2 (или 20) потоках?


    1. frazer Автор
      19.10.2022 09:45

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


      1. Apoheliy
        19.10.2022 10:09

        Да, похоже это действительно конструктор.

        Тогда более общий вопрос:

        А в чём сама библиотека-то? То, что увидел по примерам кода на github: пишется своя обёртка (для своего протокола). И пишется с применением libevent и других библиотек. Т.е. awh не скрывает использование нижележащих библиотек - а этого хотелось бы.

        Возможно, что-то неправильно понял.

        Предложение: написать статью, в котором СОЗДАЁТСЯ новый протокол (не rest, не https), что-то отвлечённое. Желательно с работай на нескольких потоках (см. выше). Желательно с использованием корутин для обработки логики протокола.

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


        1. frazer Автор
          19.10.2022 10:27

          AWH это готовый клиент/сервер (не важно какой протокол) которые можно объединять, комбинировать, при этом легко например переходить от TCP к UDP или SCTP. При этом сжатие и шифрование данных уже реализовано, все это львиная доля работы в любом проекте клиент/серверной разработки, а реализация самого протокола дело тривиальное. LibEvent используется только как событийный фреймворк и не более. Библиотеки используются только стандартные, такие, какие нет смысла писать самому, например: OpenSSL, Zlib и прочее, эти же библиотеки используют все проекты, такие как Nginx или Curl, последний так и вообще использует столько зависимостей, что только их собрать уже подвиг. В отличии от остальных проектов, я реализовал статическую сборку, т.е. конечный проект будет без зависимостей вообще, как раз факт использования сторонних библиотек будет скрыт.


        1. frazer Автор
          19.10.2022 10:28

          Про реализацию своего стороннего протокола идея интересная


          1. domix32
            19.10.2022 14:51
            +1

            Так потому вас и спрашивали почему это конструктор протоколов, когда этим и не пахнет. API это конечно тоже протокол, но обычно не тот что имеют ввиду по-умолчанию


            1. frazer Автор
              19.10.2022 15:38

              Потому, что это и есть конструктор протоколов, что есть вся базовая составляющая, а реализация протокола лежит на плечах разработчика. Как вы по другому представляете конструктор протоколов? На каком-то языке разметки описать сколько байт отправляется в какой момент и куда? Так, это все тоже самое, протоколы могут быть очень сложные и реализация даже того же "ping" в каждом протоколе будет своя, и за вас ни один генератор этот код не напишет. Здесь все как я вижу придрались к заголовку поста, что это мол не конструктор, а что тогда конструктор? Хоть бы кто пояснил. Я скорее прихожу к выводу, что либо не поняли суть проекта, либо не понимают понятия конструктора или вообще не знают что хотят.


              1. eao197
                19.10.2022 18:58

                На каком-то языке разметки описать сколько байт отправляется в какой момент и куда?

                Ага.

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

                Народец не тот попался, да.

                Вашу разработку было бы правильнее назвать "конструктор сетевых сервисов". Хотя и здесь от слова "конструктор" могут быть неправильные ожидания.


                1. frazer Автор
                  19.10.2022 21:32

                  Хорошая идея с названием, мне нравится, изменил заголовок.


  1. Kreastr
    19.10.2022 10:23
    +2

    Судя по заголовку ожидал увидеть свежий взгляд на тему https://thrift.apache.org/ Ожидания не оправдались.


  1. eao197
    19.10.2022 10:42

    Простите, а где, собственно, описание конструктора?

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

    Или же у вас есть возможность декларативно описать a) формат PDU протокола и b) поток PDU протокола, а затем AWH для меня сгенерирует код по работе с сетью, по парсингу и проверке корректности PDU и пр.?


    1. frazer Автор
      19.10.2022 10:58

      Для этого есть пример который разбит на разные виды сокетов (sample) UDP/TCP/UnixSocket ...

      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;
      }

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


      1. eao197
        19.10.2022 11:07

        Из этого примера мало что понятно. Вы бы вместо того, чтобы накидывать в статью 100500 разных примеров, привели бы 1-2, но с разбором того, что делается, почему делается именно так, что происходит "за кулисами".

        Например, что это за методы active и message, почему у них именно такой формат, почему их нужно вручную привязывать к экземпляру sample.


        1. frazer Автор
          19.10.2022 11:09

          Документацию сделаю


    1. frazer Автор
      19.10.2022 11:03

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


      1. eao197
        19.10.2022 11:11
        +1

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

        Если вы в заголовок статьи выносите слово "конструктор", то у читателей возникают некоторые ожидания о того, какую функциональность предоставит этот самый "конструктор". И тут выясняется, что "конструктор" -- это разве что event-loop + еще несколько плюшек сверху. А всю основную работу по парсингу и валидации содержимого PDU нужно делать врукопашную.

        Самое сложное это именно клиент-серверное взаимодействие, шифрование и сжатие данных

        Боюсь, "у меня для вас плохие новости" (с)