diff --git a/lesson_03/ap/ap.cpp b/lesson_03/ap/ap.cpp new file mode 100644 index 0000000..b385274 --- /dev/null +++ b/lesson_03/ap/ap.cpp @@ -0,0 +1,136 @@ +/* + * ap.cpp + * + * Created on: 05 сен. 2022 г. + * Author: alexander + */ + +#include + +namespace ap +{ + +ConfigOption::ConfigOption(const std::string &longParameter, const char shortParameter, const hasArg ha) : + _longParameter(nullptr), _shortParameter(shortParameter), _ha(ha) +{ + _longParameter = new char[longParameter.length() + 1]; + strcpy(_longParameter, longParameter.c_str()); +} + +const char* ConfigOption::getLongParameter() const +{ + return _longParameter; +} + +const char& ConfigOption::getShortParameter() const +{ + return _shortParameter; +} + +const hasArg& ConfigOption::getPresenceArgument() const +{ + return _ha; +} + +Option::Option() : _set(false) +{ +} + +void Option::set() +{ + _set = true; +} + +void Option::push(const std::string &value) +{ + _values.push_back(value); +} + +std::vector& Option::getValues() +{ + return _values; +} + +bool Option::isSet() const +{ + return _set; +} + +void Hub::_createArguments(const std::vector &options, bool silence) +{ + _longOptions = new struct option[options.size() + 1]; + _sizeOptions = options.size(); + std::string temp; + if (silence) + temp.push_back(':'); + for (auto const &opt : options | boost::adaptors::indexed(0)) + { + _longOptions[opt.index()].name = opt.value().getLongParameter(); + _longOptions[opt.index()].has_arg = opt.value().getPresenceArgument(); + _longOptions[opt.index()].flag = nullptr; + _longOptions[opt.index()].val = opt.value().getShortParameter(); + + temp.push_back(opt.value().getShortParameter()); + switch (opt.value().getPresenceArgument()) + { + case hasArg::OPTIONAL: + temp.push_back(':'); + /* no break */ + case hasArg::REQUIRED: + temp.push_back(':'); + break; + case hasArg::NO: + break; + } + + _arguments[opt.value().getShortParameter()].first = false; + } + + _longOptions[options.size()].name = nullptr; + _longOptions[options.size()].has_arg = 0; + _longOptions[options.size()].flag = nullptr; + _longOptions[options.size()].val = 0; + + _shortOptions = new char[temp.size() + 1]; + strcpy(_shortOptions, temp.c_str()); +} + +Hub::Hub(const std::vector &options, bool silence) : + _longOptions(nullptr), _shortOptions(nullptr) +{ + _createArguments(options, silence); +} + +void Hub::readArguments(int argc, char *argv[], void (*_callback)()) +{ + int next_option; + while ((next_option = getopt_long(argc, argv, _shortOptions, _longOptions, nullptr)) != -1) + { + if (_arguments.count(next_option)) + { + _arguments[next_option].first = true; + _arguments[next_option].second.set(); + if (optarg) + _arguments[next_option].second.push(std::string(optarg)); + } + if (next_option == '?' && _callback) + _callback(); + } +} + +Option Hub::getOption(char key) const +{ + return _arguments.count(key) && _arguments.at(key).first ? _arguments.at(key).second : Option(); +} + +Hub::~Hub() +{ + delete[] _shortOptions; + for (size_t i = 0; i < _sizeOptions; ++i) + { + delete[] _longOptions[i].name; + } + delete[] _longOptions; +} + +} diff --git a/lesson_03/ap/ap.hpp b/lesson_03/ap/ap.hpp new file mode 100644 index 0000000..8ae0de8 --- /dev/null +++ b/lesson_03/ap/ap.hpp @@ -0,0 +1,68 @@ +/* + * ap.hpp + * + * Created on: 05 сен. 2022 г. + * Author: alexander + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace ap +{ + +enum hasArg +{ + NO, REQUIRED, OPTIONAL +}; + +typedef std::vector (*_handler)(const std::vector&); + +class ConfigOption +{ +private: + char *_longParameter; + const char _shortParameter; + const hasArg _ha; +public: + ConfigOption(const std::string &longParameter, const char shortParameter, const hasArg ha); + const char* getLongParameter() const; + const char& getShortParameter() const; + const hasArg& getPresenceArgument() const; +}; + +class Option +{ +private: + std::vector _values; + bool _set; +public: + Option(); + void push(const std::string &value); + std::vector& getValues(); + void set(); + bool isSet() const; +}; + +class Hub +{ +private: + struct option *_longOptions; + size_t _sizeOptions; + char *_shortOptions; + std::map> _arguments; + void _createArguments(const std::vector &options, bool silence); +public: + Hub(const std::vector &options, bool silence = true); + void readArguments(int argc, char *argv[], void (*_callback)() = nullptr); + Option getOption(char key) const; + ~Hub(); +}; + +} diff --git a/lesson_03/header/address.hpp b/lesson_03/header/address.hpp new file mode 100644 index 0000000..99f2772 --- /dev/null +++ b/lesson_03/header/address.hpp @@ -0,0 +1,33 @@ +/* + * address.hpp + * + * Created on: 5 сент. 2022 г. + * Author: alexander + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace zh { + +class Address +{ +private: + struct sockaddr_in _addr; + std::unique_ptr _host; + unsigned int _len; +public: + Address(); + Address(const Address &address); + Address(const unsigned short int family, const unsigned int addr, const unsigned short int port); + Address(const unsigned short int family, const std::string &addr, const unsigned short int port); + unsigned int& size(); + struct sockaddr_in& operator*(); +}; + +} diff --git a/lesson_03/header/buffer.hpp b/lesson_03/header/buffer.hpp new file mode 100644 index 0000000..1705611 --- /dev/null +++ b/lesson_03/header/buffer.hpp @@ -0,0 +1,28 @@ +/* + * buffer.hpp + * + * Created on: 7 сент. 2022 г. + * Author: alexander + */ + +#pragma once + +#include + +namespace zh +{ + +class Buffer +{ +private: + const unsigned short int _size; + std::unique_ptr _buffer; +public: + Buffer(const unsigned short int size); + const unsigned short int getSize() const; + const std::string getString() const; + void clear(); + operator char*(); +}; + +} diff --git a/lesson_03/header/client.hpp b/lesson_03/header/client.hpp new file mode 100644 index 0000000..d5e83f0 --- /dev/null +++ b/lesson_03/header/client.hpp @@ -0,0 +1,26 @@ +/* + * client.hpp + * + * Created on: 7 сент. 2022 г. + * Author: alexander + */ + +#pragma once + +#include + +namespace zh +{ + +class ClientTCP: public Server // @suppress("Class has a virtual method and non-virtual destructor") +{ +private: + bool _connect; + + void chat(); +public: + ClientTCP(const std::string &address, const unsigned short int port, const unsigned short int sizeBuffer = 1024); + void connect(); +}; + +} diff --git a/lesson_03/header/server.hpp b/lesson_03/header/server.hpp new file mode 100644 index 0000000..5c7f012 --- /dev/null +++ b/lesson_03/header/server.hpp @@ -0,0 +1,55 @@ +/* + * server.hpp + * + * Created on: 5 сент. 2022 г. + * Author: alexander + */ + +#pragma once + +//#include + +#include +#include +#include + +namespace zh +{ + +class ServerTCP; + +typedef void (*hook)(std::string buffer, ServerTCP &s); + +class ServerTCP: public Server // @suppress("Class has a virtual method and non-virtual destructor") +{ +private: + bool _bind; + int _connfd; + bool _listenLoop; + unsigned int _sizeClient; + std::unique_ptr
_client; + + class Hook + { + private: + std::string _command; + hook _handler; + public: + Hook(std::string command, hook handler); + void execute(std::string buffer, ServerTCP &s); + }; + + std::vector _hooks; + + void chat(); +public: + ServerTCP(const unsigned short int port, const unsigned short int sizeBuffer = 1024); + void registerHook(std::string command, hook handler); + void bind(); + void listen(); + void disconnect(); + void stop(); +}; + +} + diff --git a/lesson_03/header/socket.hpp b/lesson_03/header/socket.hpp new file mode 100644 index 0000000..4e29363 --- /dev/null +++ b/lesson_03/header/socket.hpp @@ -0,0 +1,26 @@ +/* + * socket.hpp + * + * Created on: 5 сент. 2022 г. + * Author: alexander + */ + +#pragma once + +#include +#include +#include + +namespace zh { + +class Socket +{ +private: + int _sockfd; +public: + Socket(const int domain, const int type, const int protocol); + operator int() const; + ~Socket(); +}; + +} diff --git a/lesson_03/header/stock.hpp b/lesson_03/header/stock.hpp new file mode 100644 index 0000000..2faeef4 --- /dev/null +++ b/lesson_03/header/stock.hpp @@ -0,0 +1,31 @@ +/* + * stock.hpp + * + * Created on: 7 сент. 2022 г. + * Author: alexander + */ + +#pragma once + +#include +#include +#include +#include + +namespace zh +{ + +class Server // @suppress("Class has a virtual method and non-virtual destructor") +{ +protected: + std::unique_ptr _socket; + std::unique_ptr
_address; + std::unique_ptr _buffer; + const unsigned short int _port; +public: + Server(const unsigned short int port, const unsigned short int sizeBuffer); + virtual void bind() {} + virtual void listen() {} +}; + +} diff --git a/lesson_03/main.cpp b/lesson_03/main.cpp new file mode 100644 index 0000000..bb9600d --- /dev/null +++ b/lesson_03/main.cpp @@ -0,0 +1,95 @@ +/* + * main.cpp + * + * Created on: 5 сент. 2022 г. + * Author: alexander + */ + +#include +#include + +#include +#include + +void print(std::string buffer, zh::ServerTCP &s) +{ + std::cout << buffer << std::endl; +} + +void stop(std::string buffer, zh::ServerTCP &s) +{ + s.stop(); +} + +void disconnect(std::string buffer, zh::ServerTCP &s) +{ + s.disconnect(); +} + +void server(const int port) +{ + zh::ServerTCP tcpServer(port); + + tcpServer.registerHook("", print); + tcpServer.registerHook("disconnect", disconnect); + tcpServer.registerHook("stop", stop); + + tcpServer.bind(); + tcpServer.listen(); +} + +void client(const int port, const std::string &host) +{ + zh::ClientTCP tcpClient(host, port); + tcpClient.connect(); +} + +int main(int argc, char *argv[]) +{ + ap::Hub hub({ + { "port", 'p', ap::REQUIRED }, + { "host", 'h', ap::OPTIONAL }, + { "server", 's', ap::NO }, + { "client", 'c', ap::NO } + }); + hub.readArguments(argc, argv); + + auto optionPort = hub.getOption('p'); + if (!optionPort.isSet()) + { + std::cerr << "Порт не был установлен!" << std::endl; + exit(EXIT_FAILURE); + } + + int port = std::stoi(optionPort.getValues()[0]); + + auto optionClient = hub.getOption('c'); + auto optionServer = hub.getOption('s'); + + if (optionClient.isSet()) + { + auto optionHost = hub.getOption('h'); + if (!optionHost.isSet()) + { + std::cerr << "Адрес не был установлен!" << std::endl; + exit(EXIT_FAILURE); + } + + auto host = optionHost.getValues()[0]; + + client(port, host); + } + else if (optionServer.isSet()) + server(port); + else + { + std::cerr << "Необходимо установить режим запуска:" << std::endl + << "\t-c\t--client\tЗапуск в режиме клиента" << std::endl + << "\t-s\t--server\tЗапуск в режиме сервера" << std::endl + << "\t-h\t--host\t\tУказать IP-адрес сервера" << std::endl + << "\t-p\t--port\t\tУказать порт" << std::endl; + exit(EXIT_FAILURE); + } + + return 0; +} diff --git a/lesson_03/source/address.cpp b/lesson_03/source/address.cpp new file mode 100644 index 0000000..48cf492 --- /dev/null +++ b/lesson_03/source/address.cpp @@ -0,0 +1,56 @@ +/* + * address.cpp + * + * Created on: 5 сент. 2022 г. + * Author: alexander + */ + +#include + +namespace zh { + +Address::Address() +{ + memset(&_addr, 0, sizeof(_addr)); + _len = sizeof(_addr); +} + +Address::Address(const Address &address) +{ + _addr = address._addr; + _len = address._len; +} + +Address::Address(const unsigned short int family, const unsigned int addr, const unsigned short int port) +{ + memset(&_addr, 0, sizeof(_addr)); + _len = sizeof(_addr); + + _addr.sin_family = family; + _addr.sin_addr.s_addr = htonl(addr); + _addr.sin_port = htons(port); +} + +Address::Address(const unsigned short int family, const std::string &addr, const unsigned short int port) +{ + memset(&_addr, 0, sizeof(_addr)); + _len = sizeof(_addr); + + _host = std::make_unique(gethostbyname(addr.c_str())); + + _addr.sin_family = family; + _addr.sin_addr.s_addr = *reinterpret_cast((*_host)->h_addr); + _addr.sin_port = htons(port); +} + +unsigned int& Address::size() +{ + return _len; +} + +struct sockaddr_in& Address::operator*() +{ + return _addr; +} + +} diff --git a/lesson_03/source/buffer.cpp b/lesson_03/source/buffer.cpp new file mode 100644 index 0000000..706ab6a --- /dev/null +++ b/lesson_03/source/buffer.cpp @@ -0,0 +1,40 @@ +/* + * buffer.cpp + * + * Created on: 7 сент. 2022 г. + * Author: alexander + */ + +#include +#include +#include + +namespace zh +{ + +Buffer::Buffer(const unsigned short int size) : _size(size) +{ + _buffer = std::make_unique(_size); +} + +const unsigned short int Buffer::getSize() const +{ + return _size; +} + +const std::string Buffer::getString() const +{ + return reinterpret_cast(_buffer.get()); +} + +void Buffer::clear() +{ + bzero(_buffer.get(), _size); +} + +Buffer::operator char*() +{ + return reinterpret_cast(_buffer.get()); +} + +} diff --git a/lesson_03/source/client.cpp b/lesson_03/source/client.cpp new file mode 100644 index 0000000..a70bd52 --- /dev/null +++ b/lesson_03/source/client.cpp @@ -0,0 +1,58 @@ +/* + * client.cpp + * + * Created on: 7 сент. 2022 г. + * Author: alexander + */ + +#include +#include +#include +#include + +namespace zh +{ + +ClientTCP::ClientTCP(const std::string &address, const unsigned short int port, const unsigned short int sizeBuffer) : Server(port, sizeBuffer), _connect(false) +{ + _socket = std::make_unique(AF_INET, SOCK_STREAM, IPPROTO_IP); + _address = std::make_unique
(AF_INET, address, port); +} + +void ClientTCP::connect() +{ + if (_connect) + { + std::cerr << "Клиентский сокет уже подключён к серверному сокету!" << std::endl; + return; + } + + if (::connect(*_socket, reinterpret_cast(&(*_address)), (*_address).size()) != 0) + std::cerr << "Не удаётся подключить клиентский сокет к серверному сокету!" << std::endl; + else + _connect = true; + + chat(); +} + +void ClientTCP::chat() +{ + Buffer *bf = &(*_buffer); + while (_connect) + { + bf->clear(); + std::cin.getline(*bf, bf->getSize()); + write(*_socket, *bf, bf->getSize()); + bf->clear(); + if (read(*_socket, *bf, bf->getSize()) > 0) + { + if (bf->getString() == "exit") + break; + else + std::cout << '\t' << bf->getString() << std::endl; + } + } + close(*_socket); +} + +} diff --git a/lesson_03/source/server.cpp b/lesson_03/source/server.cpp new file mode 100644 index 0000000..56ee3bc --- /dev/null +++ b/lesson_03/source/server.cpp @@ -0,0 +1,120 @@ +/* + * server.cpp + * + * Created on: 5 сент. 2022 г. + * Author: alexander + */ + +#include +#include +#include +#include +#include + +namespace zh +{ + +ServerTCP::Hook::Hook(std::string command, hook handler) : _command(command), _handler(handler) +{ +} + +void ServerTCP::Hook::execute(std::string buffer, ServerTCP &s) +{ + if (_command == "") + { + _handler(buffer, s); + } + else + { + if (buffer == _command) + { + _handler(buffer, s); + } + } +} + +void ServerTCP::registerHook(std::string command, hook handler) +{ + _hooks.push_back({ command, handler }); +} + +ServerTCP::ServerTCP(const unsigned short int port, const unsigned short int sizeBuffer) : Server(port, sizeBuffer), _bind(false), _connfd(-1), _listenLoop(true) +{ + _socket = std::make_unique(AF_INET, SOCK_STREAM, IPPROTO_IP); + _address = std::make_unique
(AF_INET, INADDR_ANY, port); + _sizeClient = sizeof(_client); +} + +void ServerTCP::bind() +{ + if (_bind) + { + std::cerr << "Адрес уже связан с дескриптором слушающего сокета!" << std::endl; + return; + } + + if (::bind(*_socket, reinterpret_cast(&(*_address)), (*_address).size()) != 0) + std::cerr << "Не удаётся связать адрес с дескриптором слушающего сокета!" << std::endl; + else + _bind = true; +} + +void ServerTCP::listen() +{ + if (::listen(*_socket, 0) != 0) + { + std::cerr << "Не удаётся создать очередь соединений для сокета!" << std::endl; + return; + } + + while (_bind) + { + if ((_connfd = ::accept(*_socket, reinterpret_cast(&(*_client)), &_sizeClient)) < 0) + { + std::cerr << "Не удаётся верифицировать и принять пакеты от клиента!" << std::endl; + return; + } + else + _listenLoop = true; + + // Обработка соединения с клиентом + chat(); + } +} + +void ServerTCP::chat() +{ + Buffer *bf = &(*_buffer); + while (_listenLoop) + { + bf->clear(); + int size = read(_connfd, *bf, bf->getSize()); + if (size > 0) + { + std::for_each(_hooks.begin(), _hooks.end(), [&](Hook &hook) + { + hook.execute(bf->getString(), *this); + }); + if (!_listenLoop) + write(_connfd, "exit", 4); + else + write(_connfd, "OK", 2); + } + else if (size < 0) + break; + } + close(_connfd); +} + +void ServerTCP::stop() +{ + _listenLoop = false; + _bind = 0; +} + +void ServerTCP::disconnect() +{ + _listenLoop = false; +} + +} diff --git a/lesson_03/source/socket.cpp b/lesson_03/source/socket.cpp new file mode 100644 index 0000000..ccfe2a2 --- /dev/null +++ b/lesson_03/source/socket.cpp @@ -0,0 +1,32 @@ +/* + * socket.cpp + * + * Created on: 5 сент. 2022 г. + * Author: alexander + */ + +#include +#include + +namespace zh { + +Socket::Socket(const int domain, const int type, const int protocol) +{ + if ((_sockfd = socket(domain, type, protocol)) < 0) + { + perror("socket creation failed"); + exit(EXIT_FAILURE); + } +} + +Socket::operator int() const +{ + return _sockfd; +} + +Socket::~Socket() +{ + ::close(_sockfd); +} + +} diff --git a/lesson_03/source/stock.cpp b/lesson_03/source/stock.cpp new file mode 100644 index 0000000..3e8ca0a --- /dev/null +++ b/lesson_03/source/stock.cpp @@ -0,0 +1,18 @@ +/* + * stock.cpp + * + * Created on: 7 сент. 2022 г. + * Author: alexander + */ + +#include + +namespace zh +{ + +Server::Server(const unsigned short int port, const unsigned short int sizeBuffer) : _port(port) +{ + _buffer = std::make_unique(sizeBuffer); +} + +}