From 2e69bd244f54c88acbe549959449f75ab7a17e90 Mon Sep 17 00:00:00 2001 From: Konstantin Nedikov Date: Sun, 24 Mar 2019 18:54:49 +0300 Subject: [PATCH 1/3] init work --- udp/client/CMakeLists.txt | 11 ++ udp/client/include/client_exception.h | 15 +++ udp/client/include/client_logic.h | 29 +++++ udp/client/include/io_util.h | 41 ++++++ udp/client/include/login.h | 19 +++ udp/client/include/main_cycle.h | 7 ++ udp/client/src/client_logic.cpp | 117 ++++++++++++++++++ udp/client/src/io_util.cpp | 90 ++++++++++++++ udp/client/src/login.cpp | 83 +++++++++++++ udp/client/src/main.cpp | 86 +++++++++++++ udp/client/src/main_cycle.cpp | 129 +++++++++++++++++++ udp/server/.gitkeep | 0 udp/server/CMakeLists.txt | 19 +++ udp/server/include/pools.h | 31 +++++ udp/server/include/request.hpp | 31 +++++ udp/server/include/response.hpp | 36 ++++++ udp/server/include/server.h | 38 ++++++ udp/server/include/util.hpp | 72 +++++++++++ udp/server/src/main.cpp | 27 ++++ udp/server/src/pools.cpp | 58 +++++++++ udp/server/src/server.cpp | 172 ++++++++++++++++++++++++++ 21 files changed, 1111 insertions(+) create mode 100644 udp/client/CMakeLists.txt create mode 100644 udp/client/include/client_exception.h create mode 100644 udp/client/include/client_logic.h create mode 100644 udp/client/include/io_util.h create mode 100644 udp/client/include/login.h create mode 100644 udp/client/include/main_cycle.h create mode 100644 udp/client/src/client_logic.cpp create mode 100644 udp/client/src/io_util.cpp create mode 100644 udp/client/src/login.cpp create mode 100644 udp/client/src/main.cpp create mode 100644 udp/client/src/main_cycle.cpp delete mode 100644 udp/server/.gitkeep create mode 100644 udp/server/CMakeLists.txt create mode 100644 udp/server/include/pools.h create mode 100644 udp/server/include/request.hpp create mode 100644 udp/server/include/response.hpp create mode 100644 udp/server/include/server.h create mode 100644 udp/server/include/util.hpp create mode 100644 udp/server/src/main.cpp create mode 100644 udp/server/src/pools.cpp create mode 100644 udp/server/src/server.cpp diff --git a/udp/client/CMakeLists.txt b/udp/client/CMakeLists.txt new file mode 100644 index 0000000..e719b8e --- /dev/null +++ b/udp/client/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.13) +project(client) + +set(CMAKE_CXX_STANDARD 14) + +set(SOURCE_FILES + src/main.cpp src/login.cpp src/io_util.cpp src/main_cycle.cpp src/client_logic.cpp) + +include_directories(../server/include include) + +add_executable(client ${SOURCE_FILES}) diff --git a/udp/client/include/client_exception.h b/udp/client/include/client_exception.h new file mode 100644 index 0000000..9e1915f --- /dev/null +++ b/udp/client/include/client_exception.h @@ -0,0 +1,15 @@ +#include + +#ifndef CLIENT_CLIENT_EXCEPTION_H +#define CLIENT_CLIENT_EXCEPTION_H + +#include +#include + +class client_exception : std::runtime_error { +public: + explicit client_exception(const std::string& message): runtime_error(message){}; +}; + + +#endif //CLIENT_CLIENT_EXCEPTION_H diff --git a/udp/client/include/client_logic.h b/udp/client/include/client_logic.h new file mode 100644 index 0000000..80d0d66 --- /dev/null +++ b/udp/client/include/client_logic.h @@ -0,0 +1,29 @@ +#ifndef CLIENT_LOGIC_H +#define CLIENT_LOGIC_H + +#include +#include +#include "login.h" + +std::vector get_all(Identifier& ident, int socket_descriptor); + +uint64_t get_account_info(Identifier& ident, int socket_descriptor); + +bool payment(Identifier& ident, int socket_descriptor, std::string& id, uint64_t sum); + +std::vector> get_payment_results(Identifier& ident, int socket_descriptor); + +std::vector> get_request_for_payments(Identifier& ident, int socket_descriptor); + +bool ask_for_payment(Identifier& ident, int socket_descriptor, std::string& id, uint64_t sum); + +enum class confirm_payment_status { + CONFIRMED_SUCCESSFULLY, + CONFIRMATION_FAILED, + REJECTED_SUCCESSFULLY, + REJECTION_FAILED +}; + +confirm_payment_status confirm_payment(Identifier& ident, int socket_descriptor, std::string& id, uint64_t sum); + +#endif //CLIENT_LOGIC_H diff --git a/udp/client/include/io_util.h b/udp/client/include/io_util.h new file mode 100644 index 0000000..de20790 --- /dev/null +++ b/udp/client/include/io_util.h @@ -0,0 +1,41 @@ +#ifndef CLIENT_UTIL_H +#define CLIENT_UTIL_H + +#include +#include + +#include +#include +#include +#include +#include "client_exception.h" + +void write_to_socket(int socket_descriptor, const void *buf, size_t size); + +void read_from_socket(int socket_descriptor, void *buf, size_t size); + +void print(const std::string &s); + +void println(const std::string &s); + +void error(const std::string &s); + +void error(const std::string &type, const std::string &s); + +pstp_response_header read_header(int socket_descriptor); + +template +T read_thing(int socket_descriptor) { + T thing; + read_from_socket(socket_descriptor, (char *) &thing, sizeof(T)); + + return thing; +} + +std::string read_until_zero(int* ptr, char* buffer); + +char* read_bytes(int socket_descriptor, size_t size); + +void read_string_pair_vector(char *buffer, uint32_t num_of_elements, std::vector>& dest); + +#endif //CLIENT_UTIL_H diff --git a/udp/client/include/login.h b/udp/client/include/login.h new file mode 100644 index 0000000..e97dd71 --- /dev/null +++ b/udp/client/include/login.h @@ -0,0 +1,19 @@ +#ifndef CLIENT_LOGIN_H +#define CLIENT_LOGIN_H + +struct Identifier { + Identifier(std::string& login, std::string& password) : login(login), password(password) {} + + std::string login; + std::string password; +}; + +std::string has_account(); + +Identifier read_login_and_password(); +Identifier login(int socket_descriptor); + +std::string read_password(); +Identifier registration(int socket_descriptor); + +#endif //CLIENT_LOGIN_H diff --git a/udp/client/include/main_cycle.h b/udp/client/include/main_cycle.h new file mode 100644 index 0000000..884bc55 --- /dev/null +++ b/udp/client/include/main_cycle.h @@ -0,0 +1,7 @@ +#ifndef CLIENT_MAIN_CYCLE_H +#define CLIENT_MAIN_CYCLE_H + +void print_help(); +void main_cycle(Identifier ident, int socket_descriptor); + +#endif //CLIENT_MAIN_CYCLE_H diff --git a/udp/client/src/client_logic.cpp b/udp/client/src/client_logic.cpp new file mode 100644 index 0000000..07e3850 --- /dev/null +++ b/udp/client/src/client_logic.cpp @@ -0,0 +1,117 @@ +#include +#include +#include +#include + + +std::vector get_all(Identifier& ident, int socket_descriptor) { + struct pstp_get_all_request get_all_request(ident.login, ident.password); + write_to_socket(socket_descriptor, (char *) &get_all_request, sizeof(get_all_request)); + + struct pstp_response_header get_all_response_header = read_header(socket_descriptor); + + auto num_of_accounts = read_thing(socket_descriptor); + + size_t acc_size_byte = get_all_response_header.content_size - sizeof(uint32_t); + char* buffer = read_bytes(socket_descriptor, acc_size_byte); + + int ptr = 0; + std::vector result(num_of_accounts); + for (int i = 0; i < num_of_accounts; i++) { + result[i] = read_until_zero(&ptr, buffer); + } + delete[] buffer; + + return result; +} + +uint64_t get_account_info(Identifier& ident, int socket_descriptor) { + struct pstp_account_info_request account_info_request(ident.login, ident.password); + write_to_socket(socket_descriptor, (char *) &account_info_request, sizeof(account_info_request)); + + struct pstp_response_header account_info_response_header = read_header(socket_descriptor); + + uint32_t content_size = account_info_response_header.content_size; + if (content_size != sizeof(uint64_t)) { + error("header contract violation!"); + } + + return read_thing(socket_descriptor); +} + +bool payment(Identifier& ident, int socket_descriptor, std::string &id, uint64_t sum) { + struct pstp_payment_request payment_request(ident.login, ident.password, id, sum); + write_to_socket(socket_descriptor, (char *) &payment_request, sizeof(payment_request)); + + struct pstp_response_header payment_response_header = read_header(socket_descriptor); + return payment_response_header.code == OK; +} + +std::vector> get_payment_results(Identifier& ident, int socket_descriptor) { + std::vector> result; + + struct pstp_payment_results_request results_request(ident.login, ident.password); + write_to_socket(socket_descriptor, (char *) &results_request, sizeof(results_request)); + + struct pstp_response_header payment_response_header = read_header(socket_descriptor); + + auto num_of_results = read_thing(socket_descriptor); + + if (num_of_results != 0) { + size_t res_size_byte = payment_response_header.content_size - sizeof(uint32_t); + char* buffer = read_bytes(socket_descriptor, res_size_byte); + read_string_pair_vector(buffer, num_of_results, result); + delete[] buffer; + } + return result; +} + +std::vector> get_request_for_payments(Identifier& ident, int socket_descriptor) { + std::vector> result; + + struct pstp_get_requests_for_payments_request get_rfp_request(ident.login, ident.password); + write_to_socket(socket_descriptor, (char *) &get_rfp_request, sizeof(get_rfp_request)); + + struct pstp_response_header get_request_for_payments_response_header = read_header(socket_descriptor); + + auto num_of_requests = read_thing(socket_descriptor); + + if (num_of_requests != 0) { + size_t req_size_byte = get_request_for_payments_response_header.content_size - sizeof(uint32_t); + char* buffer = read_bytes(socket_descriptor, req_size_byte); + read_string_pair_vector(buffer, num_of_requests, result); + delete[] buffer; + } + + return result; +} + +bool ask_for_payment(Identifier& ident, int socket_descriptor, std::string &id, uint64_t sum) { + struct pstp_ask_for_payment_request ask_for_payment_request(ident.login, ident.password, id, sum); + write_to_socket(socket_descriptor, (char *) &ask_for_payment_request, sizeof(ask_for_payment_request)); + + struct pstp_response_header payment_response_header = read_header(socket_descriptor); + + return payment_response_header.code == OK; +} + +confirm_payment_status confirm_payment(Identifier& ident, int socket_descriptor, std::string &id, uint64_t sum) { + struct pstp_confirm_payment_request confirm_payment_request(ident.login, ident.password, id, sum); + write_to_socket(socket_descriptor, (char *) &confirm_payment_request, sizeof(confirm_payment_request)); + + struct pstp_response_header payment_response_header = read_header(socket_descriptor); + + if (sum == 0) { + if (payment_response_header.code == OK) { + return confirm_payment_status::REJECTED_SUCCESSFULLY; + } else { + return confirm_payment_status::REJECTION_FAILED; + } + } else { + if (payment_response_header.code == OK) { + return confirm_payment_status::CONFIRMED_SUCCESSFULLY; + } else { + return confirm_payment_status::CONFIRMATION_FAILED; + } + } +} diff --git a/udp/client/src/io_util.cpp b/udp/client/src/io_util.cpp new file mode 100644 index 0000000..abb8e85 --- /dev/null +++ b/udp/client/src/io_util.cpp @@ -0,0 +1,90 @@ +#include +#include + +void write_to_socket(int socket_descriptor, const void *buf, size_t size) { + ssize_t n = write(socket_descriptor, buf, size); + if (n < 0) { + error("write", "failed to write to socket!"); + } + + if (n != size) { + error("write", "unexpected end of socket! (Maybe server disconnect?)"); + } +} + +void read_from_socket(int socket_descriptor, void *buf, size_t size) { + ssize_t read_bytes = 0; + while (read_bytes < size) { + ssize_t received = read(socket_descriptor, (char *)buf + read_bytes, size - read_bytes); + if (received < 0) { + if (errno == EINTR) { + continue; + } else { + error("read", "failed to read from socket!"); + } + } else if (received == 0) { + error("read", "unexpected end of socket! (Maybe server disconnect?)"); + } + read_bytes += received; + } +} + + +void print(const std::string &s) { + std::cout << s; +} + +void println(const std::string &s) { + print(s + "\n"); +} + +void error(const std::string &s) { + println("Error: " + s); + throw client_exception(s); +} + +void error(const std::string &type, const std::string &s) { + error(type + ": " + s); +} + +pstp_response_header read_header(int socket_descriptor) { + struct pstp_response_header response_header; + read_from_socket(socket_descriptor, (char *) &response_header, sizeof(response_header)); + + if (response_header.code == INVALID_PASSWORD) { + error("read header", "invalid combination of login-password!"); + } + + return response_header; +} + +std::string read_until_zero(int* ptr, char* buffer) { + std::string dest(buffer + *ptr); + *ptr += dest.size() + 1; + return dest; +} + +char *read_bytes(int socket_descriptor, size_t size) { + char* buffer = new char[size]; + + try { + read_from_socket(socket_descriptor, buffer, size); + } catch (client_exception& e) { + delete[] buffer; + throw e; + } + + return buffer; +} + +void read_string_pair_vector(char *buffer, uint32_t num_of_elements, std::vector>& dest) { + dest.resize(num_of_elements); + + int ptr = 0; + for (int i = 0; i < num_of_elements; i++) { + std::string first = read_until_zero(&ptr, buffer); + std::string second = read_until_zero(&ptr, buffer); + dest[i] = {first, second}; + } +} + diff --git a/udp/client/src/login.cpp b/udp/client/src/login.cpp new file mode 100644 index 0000000..6b9330c --- /dev/null +++ b/udp/client/src/login.cpp @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include "login.h" + +std::string has_account() { + std::cout << "Do you have an account?[y/n]\n"; + std::string answer; + std::cin >> answer; + + while (answer != "y" && answer != "n" && !std::cin.eof()) { + std::cout << "No, you're doing it wrong!\n"; + std::cout << "Write `y` if you have account; write `n` if you don't.\n"; + std::cin >> answer; + } + return answer; +} + +Identifier read_login_and_password() { + std::cout << "Enter your login and password: \n"; + + std::string login_and_password; + std::cin.ignore(std::numeric_limits::max(), '\n'); + std::getline(std::cin, login_and_password); + unsigned long delimiter = (login_and_password.find(' ')); + std::string login = login_and_password.substr(0, delimiter); + std::string password = login_and_password.substr(delimiter + 1); + + return Identifier(login, password); +} + +Identifier login(int socket_descriptor) { + Identifier ident = read_login_and_password(); + struct pstp_check_login_request login_request(ident.login, ident.password); + write_to_socket(socket_descriptor, (char *) &login_request, sizeof(login_request)); + + struct pstp_response_header login_response_header; + read_from_socket(socket_descriptor, (char *) &login_response_header, sizeof(login_response_header)); + + if (login_response_header.code == INVALID_PASSWORD) { + error("invalid combination of login-password!"); + } else if (login_response_header.code != OK) { + error("unknown error."); + } + + return ident; +} + +std::string read_password() { + std::cout << "Enter your password (max size - 63): \n"; + + std::string password; + std::cin.ignore(std::numeric_limits::max(), '\n'); + std::getline(std::cin, password); + + return password; +} + +Identifier registration(int socket_descriptor) { + std::string password = read_password(); + struct pstp_register_request register_request(password); + write_to_socket(socket_descriptor, (char *) ®ister_request, sizeof(register_request)); + + struct pstp_response_header register_response_header; + read_from_socket(socket_descriptor, (char *) ®ister_response_header, sizeof(register_response_header)); + + if (register_response_header.code == INVALID_PASSWORD) { + error("invalid password! (Perhaps, it was too big!)"); + } else if (register_response_header.code != OK) { + error("unknown error."); + } + + char buffer[TEXT_UNIT_SIZE]; + bzero(buffer, TEXT_UNIT_SIZE); + read_from_socket(socket_descriptor, buffer, register_response_header.content_size); + + std::string login(buffer); + + return Identifier(login, password); +} + diff --git a/udp/client/src/main.cpp b/udp/client/src/main.cpp new file mode 100644 index 0000000..ee269c3 --- /dev/null +++ b/udp/client/src/main.cpp @@ -0,0 +1,86 @@ +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include "login.h" +#include "io_util.h" +#include "main_cycle.h" + +int connect(std::string& hostname, uint16_t port_number) { + int socket_descriptor = socket(AF_INET, SOCK_STREAM, 0); + if (socket_descriptor < 0) { + error("Failed to open the magic gates to Internet!"); + } + + struct hostent *server = gethostbyname(hostname.c_str()); + + if (server == nullptr) { + close(socket_descriptor); + error("no such host"); + } + + struct sockaddr_in server_addr{}; + bzero((char *) &server_addr, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + bcopy(server->h_addr, (char *) &server_addr.sin_addr.s_addr, (size_t) server->h_length); + server_addr.sin_port = htons(port_number); + + if (connect(socket_descriptor, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { + close(socket_descriptor); + error("connection failed (for some reason)!"); + } + + return socket_descriptor; +} + +Identifier get_identifier(int socket_descriptor) { + std::string answer = has_account(); + if (answer == "y") { + return login(socket_descriptor); + } else if (answer == "n") { + return registration(socket_descriptor); + } else { + error("unknown answer!"); + } +} + +int main(int argc, char *argv[]) { + std::cout << "Hello!\n"; + + if (argc < 3) { + error("hostname and port number are required!"); + } + + println("Establishing connection..."); + + std::string hostname = argv[1]; + auto port_number = (uint16_t) atoi(argv[2]); // NOLINT(cert-err34-c) + + int socket_descriptor = connect(hostname, port_number); + + println("Connection established!"); + + try { + Identifier ident = get_identifier(socket_descriptor); + println("Welcome, user " + ident.login + "!"); + + println(""); + + print_help(); + main_cycle(ident, socket_descriptor); + close(socket_descriptor); + } catch (client_exception& e) { + close(socket_descriptor); + throw e; + } + + return 0; +} \ No newline at end of file diff --git a/udp/client/src/main_cycle.cpp b/udp/client/src/main_cycle.cpp new file mode 100644 index 0000000..978a2d7 --- /dev/null +++ b/udp/client/src/main_cycle.cpp @@ -0,0 +1,129 @@ +#include +#include +#include + +#include "main_cycle.h" +#include "io_util.h" +#include "client_logic.h" + +void print_help() { + println("Functions list:"); + + println("WI - to get wallet info;"); + + println("PAY - to pay to user with receiver-id sum;"); + + println("RES - to see all unseen payment requests' results;"); + + println("ASK - to ask user with receiver-id for sum;"); + println("RQT - to see all requests of payment to your user;"); + println("ARQ - " + "confirm request of payment by user with receiver-id for sum (sum must be the same as user requested), " + "or reject it (then sum is 0);"); + + println("GA - to get list of all wallets;"); + + println("H - to show all functions;"); + println("D - to disconnect and exit."); + + println(""); +} + + + +void main_cycle(Identifier ident, int socket_descriptor) { + while (true) { + println("Enter command (h for help): "); + std::string code; + std::cin >> code; + + if (code == "D") { + println("Bye!"); + break; + } else if (code == "H") { + print_help(); + } else if (code == "GA") { + auto result = get_all(ident, socket_descriptor); + + for (std::string& account: result) { + std::cout << "Account name: " << account << std::endl; + } + } else if (code == "WI") { + auto sum = get_account_info(ident, socket_descriptor); + + println("Your login: " + ident.login); + println("You have " + std::to_string(sum) + " kukareks!"); + } else if (code == "PAY") { + std::string user_id; + uint64_t sum; + std::cin >> user_id >> sum; + + if (payment(ident, socket_descriptor, user_id, sum)) { + println("Payment succeeded!"); + } else { + println("Payment failed!"); + } + } else if (code == "RES") { + auto result = get_payment_results(ident, socket_descriptor); + + if (result.empty()) { + println("No results!"); + } else { + for (const auto &payment: result) { + std::string acc = payment.first; + std::string amount = payment.second; + + print("Account " + acc + " "); + if (amount != "0") { + println("payed: " + amount); + } else { + println(": payment failed or was rejected."); + } + } + } + } else if (code == "RQT") { + auto result = get_request_for_payments(ident, socket_descriptor); + + if (result.empty()) { + println("No requests!"); + } else { + for (const auto &request: result) { + println("Request from user " + request.first + " on sum " + request.second); + } + } + } else if (code == "ASK") { + std::string user_id; + uint64_t sum; + std::cin >> user_id >> sum; + + if (ask_for_payment(ident, socket_descriptor, user_id, sum)) { + println("Request was sent! When user answers, payment result will be available."); + } else { + println("Request failed! Check if user-id was real or sum is correct."); + } + } else if (code == "ARQ") { + std::string user_id; + uint64_t sum; + std::cin >> user_id >> sum; + auto result = confirm_payment(ident, socket_descriptor, user_id, sum); + + switch (result) { + case confirm_payment_status::CONFIRMED_SUCCESSFULLY: + println("Payment confirmation succeeded!"); + break; + case confirm_payment_status::CONFIRMATION_FAILED: + println("Payment confirmation failed!"); + break; + case confirm_payment_status::REJECTED_SUCCESSFULLY: + println("Payment successfully rejected!"); + break; + case confirm_payment_status::REJECTION_FAILED: + println("Something went wrong while rejecting!"); + break; + } + } else { + println("No such command!"); + } + } +} + diff --git a/udp/server/.gitkeep b/udp/server/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/udp/server/CMakeLists.txt b/udp/server/CMakeLists.txt new file mode 100644 index 0000000..1f9047e --- /dev/null +++ b/udp/server/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.13) +project(server) + +set(CMAKE_CXX_STANDARD 14) + +set(SOURCE_FILES + src/main.cpp include/response.hpp include/request.hpp src/server.cpp include/server.h include/util.hpp src/pools.cpp include/pools.h) + +include_directories(include) + +add_executable(server ${SOURCE_FILES}) + +find_package(Threads REQUIRED) +if(THREADS_HAVE_PTHREAD_ARG) + target_compile_options(server PUBLIC "-pthread") +endif() +if(CMAKE_THREAD_LIBS_INIT) + target_link_libraries(server "${CMAKE_THREAD_LIBS_INIT}") +endif() \ No newline at end of file diff --git a/udp/server/include/pools.h b/udp/server/include/pools.h new file mode 100644 index 0000000..eae46f1 --- /dev/null +++ b/udp/server/include/pools.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "response.hpp" + +class socket_int_pool { +public: + void insert(int socket_descriptor, int id, std::thread* thread); + void remove(int socket_descriptor, int id); + void clear(); +private: + std::mutex lock; + std::map, std::thread*> pool; +}; + +bool operator <(const struct sockaddr_in& x, const struct sockaddr_in& y); + +class long_computation_response_pool { +public: + void insert(struct sockaddr_in client_addr, int id, int type, dctp_response_header response); + dctp_response_header get(struct sockaddr_in client_addr, int id, int type); + bool contains(struct sockaddr_in client_addr, int id, int type); + void clear(); +private: + std::mutex lock; + std::map, dctp_response_header> pool; +}; diff --git a/udp/server/include/request.hpp b/udp/server/include/request.hpp new file mode 100644 index 0000000..2682e83 --- /dev/null +++ b/udp/server/include/request.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +#pragma pack(push, 1) + +struct dctp_request_header { + uint8_t type; + uint32_t id; + int64_t first_operand; + int64_t second_operand; +}; + +#pragma pack(pop) + +enum request_type { + PLUS = 1, + MINUS, + MULT, + DIV, + SQRT, + FACT, + LONG_COMPUTATION_RESULT +}; + +std::string request_to_string(struct dctp_request_header request) { + return "Id: " + std::to_string(request.id) + + "; type: " + std::to_string(request.type) + + "; first operand: " + std::to_string(request.first_operand) + + "; second operand: " + std::to_string(request.second_operand); +} \ No newline at end of file diff --git a/udp/server/include/response.hpp b/udp/server/include/response.hpp new file mode 100644 index 0000000..ae7ac9b --- /dev/null +++ b/udp/server/include/response.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +#pragma pack(push, 1) + +struct dctp_response_header { + uint8_t return_code; + uint8_t operation_type; + uint32_t id; + int64_t result; +}; + +#pragma pack(pop) + +enum return_code { + OK = 0, + WAIT_FOR_RESULT, + OVERFLOW, + DIV_BY_ZERO, + FACT_OF_NEGATIVE, + SQRT_OF_NEGATIVE, + UNKNOWN_OPERATION +}; + +enum operation_type { + FAST = 1, + SLOW +}; + +inline std::string response_to_string(struct dctp_response_header response) { + return "Return code: " + std::to_string(response.return_code) + + "; type: " + std::to_string(response.operation_type) + + "; id: " + std::to_string(response.id) + + "; result: " + std::to_string(response.result); +} diff --git a/udp/server/include/server.h b/udp/server/include/server.h new file mode 100644 index 0000000..99bfe0b --- /dev/null +++ b/udp/server/include/server.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include +#include "pools.h" + +class server { +public: + explicit server(uint16_t port); + + ~server(); + + void wait_for_clients(); + + void client_handler(struct dctp_request_header request); + dctp_response_header handle_new_request(struct dctp_request_header request); + struct dctp_response_header handle_slow_function( + struct dctp_request_header request, + void(server::* process)(int, struct dctp_request_header) + ); + + void process_sqrt(int socket_descriptor, struct dctp_request_header request); + + void process_fact(int socket_descriptor, struct dctp_request_header request); +private: + uint16_t port_number; + int socket_descriptor; + sockaddr_in server_addr{}; + sockaddr_in client_addr{}; + bool isInitialized = false; + bool isTerminated = false; + + socket_int_pool slow_ops_pool; + long_computation_response_pool computation_response_pool; +}; + diff --git a/udp/server/include/util.hpp b/udp/server/include/util.hpp new file mode 100644 index 0000000..222a9e2 --- /dev/null +++ b/udp/server/include/util.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include + +void log(const std::string &s) { + std::cout << s << std::endl; +} + +void log_error(const std::string &s) { + std::cerr << "Error: " << s << std::endl; +} + +ssize_t socket_write_response(int socket_descriptor, struct dctp_response_header& response, sockaddr_in& client_addr) { + socklen_t client_size = sizeof(client_addr); + ssize_t sent_bytes = sendto( + socket_descriptor, + &response, + sizeof(response), + 0, + (struct sockaddr *) &client_addr, + client_size + ); + + log("Server: sent to socket " + + std::to_string(socket_descriptor) + + " response (size = " + std::to_string(sent_bytes) + "): " + + response_to_string(response)); + + if (sent_bytes < 0) { + log_error("can't write to socket"); + return -1; + } + if (sent_bytes == 0) { + log_error("socket has closed."); + return -1; + } + if (sent_bytes > 0 && sent_bytes != sizeof(response)) { + log_error("data was not written fully."); + return -1; + } + + return sent_bytes; +} + +ssize_t socket_read_request(int socket_descriptor, struct dctp_request_header& request, sockaddr_in& client_addr) { + socklen_t client_size = sizeof(client_addr); + ssize_t read_bytes = recvfrom( + socket_descriptor, + (void *) &request, + sizeof(request), + 0, + (struct sockaddr *) &client_addr, + &client_size + ); + + log("Server: socket " + std::to_string(socket_descriptor) + " sent request: " + request_to_string(request)); + + if (read_bytes < 0) { + log_error("error while reading request"); + return -1; + } + if (read_bytes == 0) { + log_error("socket has closed."); + return -1; + } + if (read_bytes > 0 && read_bytes != sizeof(request)) { + log_error("data was not read fully (read " + std::to_string(read_bytes) + " bytes)."); + return -1; + } + + return read_bytes; +} \ No newline at end of file diff --git a/udp/server/src/main.cpp b/udp/server/src/main.cpp new file mode 100644 index 0000000..5c72320 --- /dev/null +++ b/udp/server/src/main.cpp @@ -0,0 +1,27 @@ +#include +#include + +#include "server.h" + +server* server_pointer; +void signal_handler( int signum ) { + std::cout << "Interrupt signal (" << signum << ") received.\n"; + + delete server_pointer; + exit(signum); +} + +int main(int argc, char **argv) { + std::signal(SIGINT, signal_handler); + std::signal(SIGTERM, signal_handler); + + uint16_t port_number = 22229; + if (argc > 1) { + port_number = static_cast(atoi(argv[1])); // NOLINT(cert-err34-c) + } + + server_pointer = new server(port_number); + server_pointer->wait_for_clients(); + + return 0; +} \ No newline at end of file diff --git a/udp/server/src/pools.cpp b/udp/server/src/pools.cpp new file mode 100644 index 0000000..e31e1e2 --- /dev/null +++ b/udp/server/src/pools.cpp @@ -0,0 +1,58 @@ +#include + +#include +#include +#include + +void socket_int_pool::insert(int socket_descriptor, int id, std::thread *thread) { + lock.lock(); + pool[{socket_descriptor, id}] = thread; + lock.unlock(); +} + +void socket_int_pool::remove(int socket_descriptor, int id) { + lock.lock(); + pool.erase({socket_descriptor, id}); + lock.unlock(); +} + +void socket_int_pool::clear() { + for (auto socket_id_thread : this->pool) { + auto socket_and_id = socket_id_thread.first; + close(socket_and_id.first); + std::thread* slow_op_thread = socket_id_thread.second; + slow_op_thread->join(); + } + this->pool.clear(); +} + + +bool operator <(const struct sockaddr_in& x, const struct sockaddr_in& y) { + return x.sin_addr.s_addr < y.sin_addr.s_addr || x.sin_port < y.sin_port; +} + +void long_computation_response_pool::insert(sockaddr_in client_addr, int id, int type, dctp_response_header response) { + lock.lock(); + pool[{client_addr, id, type}] = response; + lock.unlock(); +} + +dctp_response_header long_computation_response_pool::get(struct sockaddr_in client_addr, int id, int type) { + dctp_response_header response{}; + lock.lock(); + response = pool[{client_addr, id, type}]; + lock.unlock(); + return response; +} + +void long_computation_response_pool::clear() { + this->pool.clear(); +} + +bool long_computation_response_pool::contains(struct sockaddr_in client_addr, int id, int type) { + bool result; + lock.lock(); + result = pool.find(std::make_tuple(client_addr, id, type)) != pool.end(); + lock.unlock(); + return result; +} diff --git a/udp/server/src/server.cpp b/udp/server/src/server.cpp new file mode 100644 index 0000000..8100924 --- /dev/null +++ b/udp/server/src/server.cpp @@ -0,0 +1,172 @@ +#include "server.h" + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + + +server::server(uint16_t port_number) : port_number(port_number) { + log("Server: initialization begin."); + + socket_descriptor = socket(AF_INET, SOCK_DGRAM, 0); + if (socket_descriptor < 0) { + log_error("can't open socket."); + return; + } + int k = 1; + if (setsockopt(socket_descriptor, SOL_SOCKET, SO_REUSEADDR, &k, sizeof(int)) < 0) { + log_error("setsockopt(SO_REUSEADDR) failed"); + } + + bzero((char *) &server_addr, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(port_number); + + if (bind(socket_descriptor, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { + log_error("can't bind."); + return; + } + isInitialized = true; + + log("Server: initialization success."); +} + +server::~server() { + log("Server: destruction begin."); + this->isTerminated = true; + close(socket_descriptor); + + this->slow_ops_pool.clear(); + this->computation_response_pool.clear(); + + log("Server: destruction success."); +} + +void server::wait_for_clients() { + if (!this->isInitialized) { + return; + } + + while (!this->isTerminated) { + dctp_request_header request{}; + if (socket_read_request(socket_descriptor, request, client_addr) < 0) { + continue; + } + + log("Server: new client connected. " + std::to_string(client_addr.sin_addr.s_addr)); + client_handler(request); + log("Server: stopped work with client. " + std::to_string(client_addr.sin_addr.s_addr)); + } +} + +void server::client_handler(struct dctp_request_header request) { + struct dctp_response_header response{}; + if (computation_response_pool.contains(client_addr, request.id, request.type)) { + response = computation_response_pool.get(client_addr, request.id, request.type); + } else { + response = handle_new_request(request); + computation_response_pool.insert(client_addr, request.id, request.type, response); + } + + socket_write_response(socket_descriptor, response, client_addr); +} + +dctp_response_header server::handle_new_request(struct dctp_request_header request) { + struct dctp_response_header response{}; + int64_t result; + switch (request.type) { + case PLUS: + result = request.first_operand + request.second_operand; + response = {OK, FAST, request.id, result}; + break; + case MINUS: + result = request.first_operand - request.second_operand; + response = {OK, FAST, request.id, result}; + break; + case MULT: + result = request.first_operand * request.second_operand; + response = {OK, FAST, request.id, result}; + break; + case DIV: + if (request.second_operand == 0) { + response = {DIV_BY_ZERO, FAST, request.id, 0}; + } else { + result = request.first_operand / request.second_operand; + response = {OK, FAST, request.id, result}; + } + break; + case FACT: + response = handle_slow_function(request, &server::process_fact); + break; + case SQRT: + response = handle_slow_function(request, &server::process_sqrt); + break; + case LONG_COMPUTATION_RESULT: + // If it's not in computation pool, then it doesn't exists. + response = {UNKNOWN_OPERATION, SLOW, request.id, 0}; + break; + default: + response = {UNKNOWN_OPERATION, FAST, request.id, 0}; + break; + } + + return response; +} + +struct dctp_response_header server::handle_slow_function( + struct dctp_request_header request, + void(server::* process)(int, struct dctp_request_header) + ) { + struct dctp_response_header response{WAIT_FOR_RESULT, SLOW, request.id, 0}; + computation_response_pool.insert(client_addr, request.id, request_type::LONG_COMPUTATION_RESULT, response); + + std::thread* thread = new std::thread(process, this, socket_descriptor, request); + slow_ops_pool.insert(socket_descriptor, request.id, thread); + return response; +} + +void server::process_sqrt(int socket_descriptor, struct dctp_request_header request) { + sleep(3); + struct dctp_response_header response{}; + if (request.first_operand < 0) { + response = {SQRT_OF_NEGATIVE, SLOW, request.id, 0}; + } else { + auto result = static_cast(sqrt(request.first_operand)); + response = {OK, SLOW, request.id, result}; + } + + computation_response_pool.insert(client_addr, request.id, request_type::LONG_COMPUTATION_RESULT, response); + slow_ops_pool.remove(socket_descriptor, request.id); +} + +void server::process_fact(int socket_descriptor, struct dctp_request_header request) { + sleep(3); + struct dctp_response_header response{}; + if (request.first_operand < 0) { + response = {FACT_OF_NEGATIVE, SLOW, request.id, 0}; + } else if (request.first_operand > 20) { + response = {OVERFLOW, SLOW, request.id, 0}; + } else { + int64_t result = 1; + for (int i = 1; i <= request.first_operand; i++) { + result *= i; + } + response = {OK, SLOW, request.id, result}; + } + + computation_response_pool.insert(client_addr, request.id, request_type::LONG_COMPUTATION_RESULT, response); + slow_ops_pool.remove(socket_descriptor, request.id); +} + From c983d835b3f08abcf7aa13056fc23e8529d97e03 Mon Sep 17 00:00:00 2001 From: Konstantin Nedikov Date: Mon, 25 Mar 2019 15:43:53 +0300 Subject: [PATCH 2/3] change client to calculator tcp --- udp/client/.gitkeep | 0 udp/client/CMakeLists.txt | 20 ++- udp/client/include/CalcuatorServerDriver.hpp | 50 +++++++ udp/client/include/CalculatorApp.hpp | 33 +++++ udp/client/include/ConcurrentQueue.hpp | 33 +++++ udp/client/include/client_exception.h | 15 --- udp/client/include/client_logic.h | 29 ----- udp/client/include/io_util.h | 41 ------ udp/client/include/login.h | 19 --- udp/client/include/main_cycle.h | 7 - udp/client/include/requests.hpp | 47 +++++++ udp/client/include/socketUtils.hpp | 36 ++++++ udp/client/src/CalcuatorServerDriver.cpp | 75 +++++++++++ udp/client/src/CalculatorApp.cpp | 94 ++++++++++++++ udp/client/src/client_logic.cpp | 117 ----------------- udp/client/src/io_util.cpp | 90 ------------- udp/client/src/login.cpp | 83 ------------ udp/client/src/main.cpp | 87 +------------ udp/client/src/main_cycle.cpp | 129 ------------------- udp/client/src/requests.cpp | 18 +++ udp/client/src/socketUtils.cpp | 80 ++++++++++++ 21 files changed, 485 insertions(+), 618 deletions(-) delete mode 100644 udp/client/.gitkeep create mode 100644 udp/client/include/CalcuatorServerDriver.hpp create mode 100644 udp/client/include/CalculatorApp.hpp create mode 100644 udp/client/include/ConcurrentQueue.hpp delete mode 100644 udp/client/include/client_exception.h delete mode 100644 udp/client/include/client_logic.h delete mode 100644 udp/client/include/io_util.h delete mode 100644 udp/client/include/login.h delete mode 100644 udp/client/include/main_cycle.h create mode 100644 udp/client/include/requests.hpp create mode 100644 udp/client/include/socketUtils.hpp create mode 100644 udp/client/src/CalcuatorServerDriver.cpp create mode 100644 udp/client/src/CalculatorApp.cpp delete mode 100644 udp/client/src/client_logic.cpp delete mode 100644 udp/client/src/io_util.cpp delete mode 100644 udp/client/src/login.cpp delete mode 100644 udp/client/src/main_cycle.cpp create mode 100644 udp/client/src/requests.cpp create mode 100644 udp/client/src/socketUtils.cpp diff --git a/udp/client/.gitkeep b/udp/client/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/udp/client/CMakeLists.txt b/udp/client/CMakeLists.txt index e719b8e..bae8c75 100644 --- a/udp/client/CMakeLists.txt +++ b/udp/client/CMakeLists.txt @@ -1,11 +1,17 @@ -cmake_minimum_required(VERSION 3.13) -project(client) +cmake_minimum_required(VERSION 2.8) +project(calculator) +set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD 14) +file(GLOB SOURCE_FILES "src/*.cpp") -set(SOURCE_FILES - src/main.cpp src/login.cpp src/io_util.cpp src/main_cycle.cpp src/client_logic.cpp) +add_executable(calculator ${SOURCE_FILES} src/CalculatorApp.cpp include/CalculatorApp.hpp src/CalcuatorServerDriver.cpp include/CalcuatorServerDriver.hpp src/main.cpp include/requests.hpp include/ConcurrentQueue.hpp src/requests.cpp) -include_directories(../server/include include) +target_include_directories(calculator PRIVATE include) -add_executable(client ${SOURCE_FILES}) +find_package(Threads REQUIRED) +if(THREADS_HAVE_PTHREAD_ARG) + target_compile_options(calculator PUBLIC "-pthread") +endif() +if(CMAKE_THREAD_LIBS_INIT) + target_link_libraries(calculator "${CMAKE_THREAD_LIBS_INIT}") +endif() diff --git a/udp/client/include/CalcuatorServerDriver.hpp b/udp/client/include/CalcuatorServerDriver.hpp new file mode 100644 index 0000000..a720266 --- /dev/null +++ b/udp/client/include/CalcuatorServerDriver.hpp @@ -0,0 +1,50 @@ +#include + +#pragma once + +#include +#include +#include +#include +#include "requests.hpp" +#include "socketUtils.hpp" +#include "ConcurrentQueue.hpp" + +class CalcuatorServerDriver { +public: + CalcuatorServerDriver(std::string host, uint16_t port) : m_host(std::move(host)), m_port(port) {} + + ~CalcuatorServerDriver(); + + void connect(); + + bool hasResult(); + + CalculatorResponse getResult(); + + void factorial(uint32_t id, int64_t arg); + + void sqrt(uint32_t id, int64_t arg); + + CalculatorResponse plus(uint32_t id, int64_t arg1, int64_t arg2); + + CalculatorResponse minus(uint32_t id, int64_t arg1, int64_t arg2); + + CalculatorResponse multiply(uint32_t id, int64_t arg1, int64_t arg2); + + CalculatorResponse divide(uint32_t id, int64_t arg1, int64_t arg2); + +private: + void sendRequest(CalculatorRequest const &request); + + CalculatorResponse getResponse(); + + void readingThreadTask(); + + ConcurrentQueue m_longResults; + ConcurrentQueue m_instantResults; + int m_socket = 0; + std::string m_host; + uint16_t m_port; + std::thread m_readingThread; +}; diff --git a/udp/client/include/CalculatorApp.hpp b/udp/client/include/CalculatorApp.hpp new file mode 100644 index 0000000..84dca42 --- /dev/null +++ b/udp/client/include/CalculatorApp.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include "CalcuatorServerDriver.hpp" +#include "socketUtils.hpp" + +class CalculatorApp { +public: + CalculatorApp(std::string const &host, uint16_t port) : m_driver(host, port) {} + + void start(); + +private: + void printPrompt(uint32_t computationId); + + void processInput(std::string &line); + + template + void printResult(uint32_t id, T const &value) { + std::cout << "Out [" << id << "]: " << value << std::endl; + } + + void printEntryMessage(); + + void printLine(std::string const &line = ""); + + uint32_t m_currentComputation = 0; + CalcuatorServerDriver m_driver; + + void printResponse(const CalculatorResponse &response); +}; \ No newline at end of file diff --git a/udp/client/include/ConcurrentQueue.hpp b/udp/client/include/ConcurrentQueue.hpp new file mode 100644 index 0000000..16639f8 --- /dev/null +++ b/udp/client/include/ConcurrentQueue.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +template +class ConcurrentQueue { +public: + void push(T const &response) { + m_queueMutex.lock(); + m_resultsQueue.push(response); + m_queueMutex.unlock(); + } + + T pop() { + m_queueMutex.lock(); + auto result = m_resultsQueue.front(); + m_resultsQueue.pop(); + m_queueMutex.unlock(); + return result; + } + + bool empty() { + m_queueMutex.lock(); + bool isEmpty = m_resultsQueue.empty(); + m_queueMutex.unlock(); + return isEmpty; + } + +private: + std::mutex m_queueMutex; + std::queue m_resultsQueue; +}; diff --git a/udp/client/include/client_exception.h b/udp/client/include/client_exception.h deleted file mode 100644 index 9e1915f..0000000 --- a/udp/client/include/client_exception.h +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#ifndef CLIENT_CLIENT_EXCEPTION_H -#define CLIENT_CLIENT_EXCEPTION_H - -#include -#include - -class client_exception : std::runtime_error { -public: - explicit client_exception(const std::string& message): runtime_error(message){}; -}; - - -#endif //CLIENT_CLIENT_EXCEPTION_H diff --git a/udp/client/include/client_logic.h b/udp/client/include/client_logic.h deleted file mode 100644 index 80d0d66..0000000 --- a/udp/client/include/client_logic.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef CLIENT_LOGIC_H -#define CLIENT_LOGIC_H - -#include -#include -#include "login.h" - -std::vector get_all(Identifier& ident, int socket_descriptor); - -uint64_t get_account_info(Identifier& ident, int socket_descriptor); - -bool payment(Identifier& ident, int socket_descriptor, std::string& id, uint64_t sum); - -std::vector> get_payment_results(Identifier& ident, int socket_descriptor); - -std::vector> get_request_for_payments(Identifier& ident, int socket_descriptor); - -bool ask_for_payment(Identifier& ident, int socket_descriptor, std::string& id, uint64_t sum); - -enum class confirm_payment_status { - CONFIRMED_SUCCESSFULLY, - CONFIRMATION_FAILED, - REJECTED_SUCCESSFULLY, - REJECTION_FAILED -}; - -confirm_payment_status confirm_payment(Identifier& ident, int socket_descriptor, std::string& id, uint64_t sum); - -#endif //CLIENT_LOGIC_H diff --git a/udp/client/include/io_util.h b/udp/client/include/io_util.h deleted file mode 100644 index de20790..0000000 --- a/udp/client/include/io_util.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef CLIENT_UTIL_H -#define CLIENT_UTIL_H - -#include -#include - -#include -#include -#include -#include -#include "client_exception.h" - -void write_to_socket(int socket_descriptor, const void *buf, size_t size); - -void read_from_socket(int socket_descriptor, void *buf, size_t size); - -void print(const std::string &s); - -void println(const std::string &s); - -void error(const std::string &s); - -void error(const std::string &type, const std::string &s); - -pstp_response_header read_header(int socket_descriptor); - -template -T read_thing(int socket_descriptor) { - T thing; - read_from_socket(socket_descriptor, (char *) &thing, sizeof(T)); - - return thing; -} - -std::string read_until_zero(int* ptr, char* buffer); - -char* read_bytes(int socket_descriptor, size_t size); - -void read_string_pair_vector(char *buffer, uint32_t num_of_elements, std::vector>& dest); - -#endif //CLIENT_UTIL_H diff --git a/udp/client/include/login.h b/udp/client/include/login.h deleted file mode 100644 index e97dd71..0000000 --- a/udp/client/include/login.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef CLIENT_LOGIN_H -#define CLIENT_LOGIN_H - -struct Identifier { - Identifier(std::string& login, std::string& password) : login(login), password(password) {} - - std::string login; - std::string password; -}; - -std::string has_account(); - -Identifier read_login_and_password(); -Identifier login(int socket_descriptor); - -std::string read_password(); -Identifier registration(int socket_descriptor); - -#endif //CLIENT_LOGIN_H diff --git a/udp/client/include/main_cycle.h b/udp/client/include/main_cycle.h deleted file mode 100644 index 884bc55..0000000 --- a/udp/client/include/main_cycle.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef CLIENT_MAIN_CYCLE_H -#define CLIENT_MAIN_CYCLE_H - -void print_help(); -void main_cycle(Identifier ident, int socket_descriptor); - -#endif //CLIENT_MAIN_CYCLE_H diff --git a/udp/client/include/requests.hpp b/udp/client/include/requests.hpp new file mode 100644 index 0000000..075a99d --- /dev/null +++ b/udp/client/include/requests.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +#pragma pack(push, 1) +struct CalculatorResponse { + uint8_t errorCode; + uint8_t operationType; + uint32_t computationId; + int64_t result; +}; +#pragma pack(pop) + +enum OperationType { + FAST = 1, + SLOW +}; + +enum ErrorCode { + OK = 0, + WAIT_FOR_RESULT, + OVERFLOW, + DIV_BY_ZERO, + FACT_OF_NEGATIVE, + SQRT_OF_NEGATIVE, +}; + +std::string errorCodeToString(uint8_t code); + +#pragma pack(push, 1) +struct CalculatorRequest { + uint8_t type; + uint32_t computationId; + int64_t firstOperand; + int64_t secondOperand; +}; +#pragma pack(pop) + +enum RequestType { + PLUS = 1, + MINUS, + MULT, + DIV, + SQRT, + FACT +}; diff --git a/udp/client/include/socketUtils.hpp b/udp/client/include/socketUtils.hpp new file mode 100644 index 0000000..03ed503 --- /dev/null +++ b/udp/client/include/socketUtils.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +void writeToSocket(int socketDescriptor, const void *buffer, size_t size); + +void readFromSocket(int socketDescriptor, uint8_t *buffer, size_t size); + +void printError(const std::string &s); + +void error(const std::string &s); + +void error(const std::string &type, const std::string &s); + +int connectToServer(std::string const &hostname, uint16_t port); + +template +void writeObject(int socketDescriptor, const T &object) { + writeToSocket(socketDescriptor, (uint8_t const *) &object, sizeof(T)); +} + +template +T readObject(int socketDescriptor) { + T object; + readFromSocket(socketDescriptor, (uint8_t *) &object, sizeof(T)); + + return object; +} + +std::string read_until_zero(int *ptr, char *buffer, size_t buffer_size); diff --git a/udp/client/src/CalcuatorServerDriver.cpp b/udp/client/src/CalcuatorServerDriver.cpp new file mode 100644 index 0000000..2513718 --- /dev/null +++ b/udp/client/src/CalcuatorServerDriver.cpp @@ -0,0 +1,75 @@ +// +// Created by karvozavr on 16/02/19. +// + +#include "CalcuatorServerDriver.hpp" + +CalcuatorServerDriver::~CalcuatorServerDriver() { + shutdown(m_socket, SHUT_RDWR); + close(m_socket); +} + +void CalcuatorServerDriver::connect() { + m_socket = connectToServer(m_host, m_port); + m_readingThread = std::thread(&CalcuatorServerDriver::readingThreadTask, this); +} + +void CalcuatorServerDriver::readingThreadTask() { + while (true) { + auto response = readObject(m_socket); + if (response.errorCode != WAIT_FOR_RESULT) { + if (response.operationType == SLOW) { + m_longResults.push(response); + } else if (response.operationType == FAST) { + m_instantResults.push(response); + } + } + } +} + +bool CalcuatorServerDriver::hasResult() { + return !m_longResults.empty(); +} + +CalculatorResponse CalcuatorServerDriver::getResult() { + return m_longResults.pop(); +} + +void CalcuatorServerDriver::factorial(uint32_t id, int64_t arg) { + sendRequest({FACT, id, arg, 0}); +} + +void CalcuatorServerDriver::sqrt(uint32_t id, int64_t arg) { + sendRequest({SQRT, id, arg, 0}); +} + +CalculatorResponse CalcuatorServerDriver::plus(uint32_t id, int64_t arg1, int64_t arg2) { + sendRequest({PLUS, id, arg1, arg2}); + return getResponse(); +} + +CalculatorResponse CalcuatorServerDriver::minus(uint32_t id, int64_t arg1, int64_t arg2) { + sendRequest({MINUS, id, arg1, arg2}); + return getResponse(); +} + +CalculatorResponse CalcuatorServerDriver::multiply(uint32_t id, int64_t arg1, int64_t arg2) { + sendRequest({MULT, id, arg1, arg2}); + return getResponse(); +} + +CalculatorResponse CalcuatorServerDriver::divide(uint32_t id, int64_t arg1, int64_t arg2) { + sendRequest({DIV, id, arg1, arg2}); + return getResponse(); +} + +void CalcuatorServerDriver::sendRequest(CalculatorRequest const &request) { + writeObject(m_socket, request); +} + +CalculatorResponse CalcuatorServerDriver::getResponse() { + while (m_instantResults.empty()) {} + return m_instantResults.pop(); +} + + diff --git a/udp/client/src/CalculatorApp.cpp b/udp/client/src/CalculatorApp.cpp new file mode 100644 index 0000000..6e4ab50 --- /dev/null +++ b/udp/client/src/CalculatorApp.cpp @@ -0,0 +1,94 @@ +#include "CalculatorApp.hpp" + +void CalculatorApp::start() { + m_driver.connect(); + + printEntryMessage(); + + std::string line; + printPrompt(m_currentComputation); + + while (std::getline(std::cin, line)) { + processInput(line); + ++m_currentComputation; + printPrompt(m_currentComputation); + } +} + +void CalculatorApp::printPrompt(uint32_t computationId) { + std::cout << "In [" << computationId << "]: "; +} + +void CalculatorApp::processInput(std::string &line) { + std::istringstream iss(line); + std::vector results((std::istream_iterator(iss)), + std::istream_iterator()); + + if (results.size() == 2 || results.size() == 3 || results.empty()) { + if (results.size() == 2) { + if (results[0] == "fact") { + m_driver.factorial(m_currentComputation, static_cast(std::stoull(results[1]))); + } else if (results[0] == "sqrt") { + m_driver.sqrt(m_currentComputation, static_cast(std::stoull(results[1]))); + } + } else if (results.size() == 3 && results[0].length() == 1) { + CalculatorResponse response{}; + switch (results[0][0]) { + case '+': + response = m_driver.plus(m_currentComputation, + static_cast(std::stoull(results[1])), + static_cast(std::stoull(results[2]))); + break; + case '-': + response = m_driver.minus(m_currentComputation, + static_cast(std::stoull(results[1])), + static_cast(std::stoull(results[2]))); + break; + case '*': + response = m_driver.multiply(m_currentComputation, + static_cast(std::stoull(results[1])), + static_cast(std::stoull(results[2]))); + break; + case '/': + response = m_driver.divide(m_currentComputation, + static_cast(std::stoull(results[1])), + static_cast(std::stoull(results[2]))); + break; + default: + std::cout << "Syntax error." << std::endl; + break; + } + + printResponse(response); + } else { + std::cout << "Syntax error." << std::endl; + } + } else { + std::cout << "Syntax error." << std::endl; + } + + printLine(); + + while (m_driver.hasResult()) { + printResponse(m_driver.getResult()); + printLine(); + } +} + +void CalculatorApp::printResponse(const CalculatorResponse &response) { + if (response.errorCode != OK) { + printResult(response.computationId, errorCodeToString(response.errorCode)); + } else { + printResult(response.computationId, response.result); + } +} + +void CalculatorApp::printEntryMessage() { + std::cout + << "Welcome!\n\tOnline calculator 1.0\n\n\tSupported operations: + - * / fact sqrt\n\tUse prefix notation (e.g + 2 3).\n" + << std::endl; +} + +void CalculatorApp::printLine(const std::string &line) { + std::cout << line << std::endl; +} diff --git a/udp/client/src/client_logic.cpp b/udp/client/src/client_logic.cpp deleted file mode 100644 index 07e3850..0000000 --- a/udp/client/src/client_logic.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include -#include -#include -#include - - -std::vector get_all(Identifier& ident, int socket_descriptor) { - struct pstp_get_all_request get_all_request(ident.login, ident.password); - write_to_socket(socket_descriptor, (char *) &get_all_request, sizeof(get_all_request)); - - struct pstp_response_header get_all_response_header = read_header(socket_descriptor); - - auto num_of_accounts = read_thing(socket_descriptor); - - size_t acc_size_byte = get_all_response_header.content_size - sizeof(uint32_t); - char* buffer = read_bytes(socket_descriptor, acc_size_byte); - - int ptr = 0; - std::vector result(num_of_accounts); - for (int i = 0; i < num_of_accounts; i++) { - result[i] = read_until_zero(&ptr, buffer); - } - delete[] buffer; - - return result; -} - -uint64_t get_account_info(Identifier& ident, int socket_descriptor) { - struct pstp_account_info_request account_info_request(ident.login, ident.password); - write_to_socket(socket_descriptor, (char *) &account_info_request, sizeof(account_info_request)); - - struct pstp_response_header account_info_response_header = read_header(socket_descriptor); - - uint32_t content_size = account_info_response_header.content_size; - if (content_size != sizeof(uint64_t)) { - error("header contract violation!"); - } - - return read_thing(socket_descriptor); -} - -bool payment(Identifier& ident, int socket_descriptor, std::string &id, uint64_t sum) { - struct pstp_payment_request payment_request(ident.login, ident.password, id, sum); - write_to_socket(socket_descriptor, (char *) &payment_request, sizeof(payment_request)); - - struct pstp_response_header payment_response_header = read_header(socket_descriptor); - return payment_response_header.code == OK; -} - -std::vector> get_payment_results(Identifier& ident, int socket_descriptor) { - std::vector> result; - - struct pstp_payment_results_request results_request(ident.login, ident.password); - write_to_socket(socket_descriptor, (char *) &results_request, sizeof(results_request)); - - struct pstp_response_header payment_response_header = read_header(socket_descriptor); - - auto num_of_results = read_thing(socket_descriptor); - - if (num_of_results != 0) { - size_t res_size_byte = payment_response_header.content_size - sizeof(uint32_t); - char* buffer = read_bytes(socket_descriptor, res_size_byte); - read_string_pair_vector(buffer, num_of_results, result); - delete[] buffer; - } - return result; -} - -std::vector> get_request_for_payments(Identifier& ident, int socket_descriptor) { - std::vector> result; - - struct pstp_get_requests_for_payments_request get_rfp_request(ident.login, ident.password); - write_to_socket(socket_descriptor, (char *) &get_rfp_request, sizeof(get_rfp_request)); - - struct pstp_response_header get_request_for_payments_response_header = read_header(socket_descriptor); - - auto num_of_requests = read_thing(socket_descriptor); - - if (num_of_requests != 0) { - size_t req_size_byte = get_request_for_payments_response_header.content_size - sizeof(uint32_t); - char* buffer = read_bytes(socket_descriptor, req_size_byte); - read_string_pair_vector(buffer, num_of_requests, result); - delete[] buffer; - } - - return result; -} - -bool ask_for_payment(Identifier& ident, int socket_descriptor, std::string &id, uint64_t sum) { - struct pstp_ask_for_payment_request ask_for_payment_request(ident.login, ident.password, id, sum); - write_to_socket(socket_descriptor, (char *) &ask_for_payment_request, sizeof(ask_for_payment_request)); - - struct pstp_response_header payment_response_header = read_header(socket_descriptor); - - return payment_response_header.code == OK; -} - -confirm_payment_status confirm_payment(Identifier& ident, int socket_descriptor, std::string &id, uint64_t sum) { - struct pstp_confirm_payment_request confirm_payment_request(ident.login, ident.password, id, sum); - write_to_socket(socket_descriptor, (char *) &confirm_payment_request, sizeof(confirm_payment_request)); - - struct pstp_response_header payment_response_header = read_header(socket_descriptor); - - if (sum == 0) { - if (payment_response_header.code == OK) { - return confirm_payment_status::REJECTED_SUCCESSFULLY; - } else { - return confirm_payment_status::REJECTION_FAILED; - } - } else { - if (payment_response_header.code == OK) { - return confirm_payment_status::CONFIRMED_SUCCESSFULLY; - } else { - return confirm_payment_status::CONFIRMATION_FAILED; - } - } -} diff --git a/udp/client/src/io_util.cpp b/udp/client/src/io_util.cpp deleted file mode 100644 index abb8e85..0000000 --- a/udp/client/src/io_util.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include -#include - -void write_to_socket(int socket_descriptor, const void *buf, size_t size) { - ssize_t n = write(socket_descriptor, buf, size); - if (n < 0) { - error("write", "failed to write to socket!"); - } - - if (n != size) { - error("write", "unexpected end of socket! (Maybe server disconnect?)"); - } -} - -void read_from_socket(int socket_descriptor, void *buf, size_t size) { - ssize_t read_bytes = 0; - while (read_bytes < size) { - ssize_t received = read(socket_descriptor, (char *)buf + read_bytes, size - read_bytes); - if (received < 0) { - if (errno == EINTR) { - continue; - } else { - error("read", "failed to read from socket!"); - } - } else if (received == 0) { - error("read", "unexpected end of socket! (Maybe server disconnect?)"); - } - read_bytes += received; - } -} - - -void print(const std::string &s) { - std::cout << s; -} - -void println(const std::string &s) { - print(s + "\n"); -} - -void error(const std::string &s) { - println("Error: " + s); - throw client_exception(s); -} - -void error(const std::string &type, const std::string &s) { - error(type + ": " + s); -} - -pstp_response_header read_header(int socket_descriptor) { - struct pstp_response_header response_header; - read_from_socket(socket_descriptor, (char *) &response_header, sizeof(response_header)); - - if (response_header.code == INVALID_PASSWORD) { - error("read header", "invalid combination of login-password!"); - } - - return response_header; -} - -std::string read_until_zero(int* ptr, char* buffer) { - std::string dest(buffer + *ptr); - *ptr += dest.size() + 1; - return dest; -} - -char *read_bytes(int socket_descriptor, size_t size) { - char* buffer = new char[size]; - - try { - read_from_socket(socket_descriptor, buffer, size); - } catch (client_exception& e) { - delete[] buffer; - throw e; - } - - return buffer; -} - -void read_string_pair_vector(char *buffer, uint32_t num_of_elements, std::vector>& dest) { - dest.resize(num_of_elements); - - int ptr = 0; - for (int i = 0; i < num_of_elements; i++) { - std::string first = read_until_zero(&ptr, buffer); - std::string second = read_until_zero(&ptr, buffer); - dest[i] = {first, second}; - } -} - diff --git a/udp/client/src/login.cpp b/udp/client/src/login.cpp deleted file mode 100644 index 6b9330c..0000000 --- a/udp/client/src/login.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include -#include -#include -#include -#include -#include "login.h" - -std::string has_account() { - std::cout << "Do you have an account?[y/n]\n"; - std::string answer; - std::cin >> answer; - - while (answer != "y" && answer != "n" && !std::cin.eof()) { - std::cout << "No, you're doing it wrong!\n"; - std::cout << "Write `y` if you have account; write `n` if you don't.\n"; - std::cin >> answer; - } - return answer; -} - -Identifier read_login_and_password() { - std::cout << "Enter your login and password: \n"; - - std::string login_and_password; - std::cin.ignore(std::numeric_limits::max(), '\n'); - std::getline(std::cin, login_and_password); - unsigned long delimiter = (login_and_password.find(' ')); - std::string login = login_and_password.substr(0, delimiter); - std::string password = login_and_password.substr(delimiter + 1); - - return Identifier(login, password); -} - -Identifier login(int socket_descriptor) { - Identifier ident = read_login_and_password(); - struct pstp_check_login_request login_request(ident.login, ident.password); - write_to_socket(socket_descriptor, (char *) &login_request, sizeof(login_request)); - - struct pstp_response_header login_response_header; - read_from_socket(socket_descriptor, (char *) &login_response_header, sizeof(login_response_header)); - - if (login_response_header.code == INVALID_PASSWORD) { - error("invalid combination of login-password!"); - } else if (login_response_header.code != OK) { - error("unknown error."); - } - - return ident; -} - -std::string read_password() { - std::cout << "Enter your password (max size - 63): \n"; - - std::string password; - std::cin.ignore(std::numeric_limits::max(), '\n'); - std::getline(std::cin, password); - - return password; -} - -Identifier registration(int socket_descriptor) { - std::string password = read_password(); - struct pstp_register_request register_request(password); - write_to_socket(socket_descriptor, (char *) ®ister_request, sizeof(register_request)); - - struct pstp_response_header register_response_header; - read_from_socket(socket_descriptor, (char *) ®ister_response_header, sizeof(register_response_header)); - - if (register_response_header.code == INVALID_PASSWORD) { - error("invalid password! (Perhaps, it was too big!)"); - } else if (register_response_header.code != OK) { - error("unknown error."); - } - - char buffer[TEXT_UNIT_SIZE]; - bzero(buffer, TEXT_UNIT_SIZE); - read_from_socket(socket_descriptor, buffer, register_response_header.content_size); - - std::string login(buffer); - - return Identifier(login, password); -} - diff --git a/udp/client/src/main.cpp b/udp/client/src/main.cpp index ee269c3..ccc4cf7 100644 --- a/udp/client/src/main.cpp +++ b/udp/client/src/main.cpp @@ -1,86 +1,11 @@ -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include "login.h" -#include "io_util.h" -#include "main_cycle.h" - -int connect(std::string& hostname, uint16_t port_number) { - int socket_descriptor = socket(AF_INET, SOCK_STREAM, 0); - if (socket_descriptor < 0) { - error("Failed to open the magic gates to Internet!"); - } - - struct hostent *server = gethostbyname(hostname.c_str()); - - if (server == nullptr) { - close(socket_descriptor); - error("no such host"); - } - - struct sockaddr_in server_addr{}; - bzero((char *) &server_addr, sizeof(server_addr)); - server_addr.sin_family = AF_INET; - bcopy(server->h_addr, (char *) &server_addr.sin_addr.s_addr, (size_t) server->h_length); - server_addr.sin_port = htons(port_number); - - if (connect(socket_descriptor, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { - close(socket_descriptor); - error("connection failed (for some reason)!"); - } - - return socket_descriptor; -} - -Identifier get_identifier(int socket_descriptor) { - std::string answer = has_account(); - if (answer == "y") { - return login(socket_descriptor); - } else if (answer == "n") { - return registration(socket_descriptor); - } else { - error("unknown answer!"); - } -} +#include +#include int main(int argc, char *argv[]) { - std::cout << "Hello!\n"; - - if (argc < 3) { - error("hostname and port number are required!"); - } - - println("Establishing connection..."); - - std::string hostname = argv[1]; - auto port_number = (uint16_t) atoi(argv[2]); // NOLINT(cert-err34-c) - - int socket_descriptor = connect(hostname, port_number); - - println("Connection established!"); - - try { - Identifier ident = get_identifier(socket_descriptor); - println("Welcome, user " + ident.login + "!"); - - println(""); - - print_help(); - main_cycle(ident, socket_descriptor); - close(socket_descriptor); - } catch (client_exception& e) { - close(socket_descriptor); - throw e; + if (argc != 3) { + error("Invalid argumets", "Usage: calculator "); } - return 0; + CalculatorApp app(std::string(argv[1]), static_cast(atoi(argv[2]))); + app.start(); } \ No newline at end of file diff --git a/udp/client/src/main_cycle.cpp b/udp/client/src/main_cycle.cpp deleted file mode 100644 index 978a2d7..0000000 --- a/udp/client/src/main_cycle.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include -#include -#include - -#include "main_cycle.h" -#include "io_util.h" -#include "client_logic.h" - -void print_help() { - println("Functions list:"); - - println("WI - to get wallet info;"); - - println("PAY - to pay to user with receiver-id sum;"); - - println("RES - to see all unseen payment requests' results;"); - - println("ASK - to ask user with receiver-id for sum;"); - println("RQT - to see all requests of payment to your user;"); - println("ARQ - " - "confirm request of payment by user with receiver-id for sum (sum must be the same as user requested), " - "or reject it (then sum is 0);"); - - println("GA - to get list of all wallets;"); - - println("H - to show all functions;"); - println("D - to disconnect and exit."); - - println(""); -} - - - -void main_cycle(Identifier ident, int socket_descriptor) { - while (true) { - println("Enter command (h for help): "); - std::string code; - std::cin >> code; - - if (code == "D") { - println("Bye!"); - break; - } else if (code == "H") { - print_help(); - } else if (code == "GA") { - auto result = get_all(ident, socket_descriptor); - - for (std::string& account: result) { - std::cout << "Account name: " << account << std::endl; - } - } else if (code == "WI") { - auto sum = get_account_info(ident, socket_descriptor); - - println("Your login: " + ident.login); - println("You have " + std::to_string(sum) + " kukareks!"); - } else if (code == "PAY") { - std::string user_id; - uint64_t sum; - std::cin >> user_id >> sum; - - if (payment(ident, socket_descriptor, user_id, sum)) { - println("Payment succeeded!"); - } else { - println("Payment failed!"); - } - } else if (code == "RES") { - auto result = get_payment_results(ident, socket_descriptor); - - if (result.empty()) { - println("No results!"); - } else { - for (const auto &payment: result) { - std::string acc = payment.first; - std::string amount = payment.second; - - print("Account " + acc + " "); - if (amount != "0") { - println("payed: " + amount); - } else { - println(": payment failed or was rejected."); - } - } - } - } else if (code == "RQT") { - auto result = get_request_for_payments(ident, socket_descriptor); - - if (result.empty()) { - println("No requests!"); - } else { - for (const auto &request: result) { - println("Request from user " + request.first + " on sum " + request.second); - } - } - } else if (code == "ASK") { - std::string user_id; - uint64_t sum; - std::cin >> user_id >> sum; - - if (ask_for_payment(ident, socket_descriptor, user_id, sum)) { - println("Request was sent! When user answers, payment result will be available."); - } else { - println("Request failed! Check if user-id was real or sum is correct."); - } - } else if (code == "ARQ") { - std::string user_id; - uint64_t sum; - std::cin >> user_id >> sum; - auto result = confirm_payment(ident, socket_descriptor, user_id, sum); - - switch (result) { - case confirm_payment_status::CONFIRMED_SUCCESSFULLY: - println("Payment confirmation succeeded!"); - break; - case confirm_payment_status::CONFIRMATION_FAILED: - println("Payment confirmation failed!"); - break; - case confirm_payment_status::REJECTED_SUCCESSFULLY: - println("Payment successfully rejected!"); - break; - case confirm_payment_status::REJECTION_FAILED: - println("Something went wrong while rejecting!"); - break; - } - } else { - println("No such command!"); - } - } -} - diff --git a/udp/client/src/requests.cpp b/udp/client/src/requests.cpp new file mode 100644 index 0000000..defabe9 --- /dev/null +++ b/udp/client/src/requests.cpp @@ -0,0 +1,18 @@ +#include "requests.hpp" + +std::string errorCodeToString(uint8_t code) { + switch (code) { + case OK: + return "OK"; + case OVERFLOW: + return "Integer overflow"; + case DIV_BY_ZERO: + return "Zero division error"; + case FACT_OF_NEGATIVE: + return "Factorial of negative number"; + case SQRT_OF_NEGATIVE: + return "Sqrt of negative number"; + default: + return "Unknown error"; + } +} \ No newline at end of file diff --git a/udp/client/src/socketUtils.cpp b/udp/client/src/socketUtils.cpp new file mode 100644 index 0000000..921cfe4 --- /dev/null +++ b/udp/client/src/socketUtils.cpp @@ -0,0 +1,80 @@ +#include "socketUtils.hpp" + +void writeToSocket(int socket_descriptor, const void *buf, size_t size) { + ssize_t n = write(socket_descriptor, buf, size); + if (n <= 0) { + error("write", "failed to write to socket!"); + } + + if (n != size) { + error("write", "unexpected end of socket! (Probably server disconnected)"); + exit(0); + } +} + +void readFromSocket(int socket_descriptor, uint8_t *buf, size_t size) { + ssize_t n; + while((n = read(socket_descriptor, buf, size)) < size) { + if (n <= 0) { + error("read", "failed to read from socket!"); + exit(0); + } + + buf += n; + size -= n; + } +} + +void printError(const std::string &s) { + std::cerr << s << std::endl; +} + +void error(const std::string &s) { + printError("Error: " + s); + exit(0); +} + +void error(const std::string &type, const std::string &s) { + printError("Error " + type + ": " + s); + exit(0); +} + +std::string read_until_zero(int* ptr, char* buffer, size_t buffer_size) { + std::string dest; + while (*ptr < buffer_size && buffer[*ptr] != '\0') { + dest += buffer[*ptr]; + (*ptr)++; + } + (*ptr)++; + + return dest; +} + +int connectToServer(std::string const &hostname, uint16_t port) { + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in serv_addr{}; + struct hostent *server; + + if (sockfd < 0) { + error("Error opening socket"); + } + + server = gethostbyname(hostname.c_str()); + + if (server == nullptr) { + error("Connection", "Error, no such host"); + } + + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + bcopy(server->h_addr, (char *) &serv_addr.sin_addr.s_addr, (size_t) server->h_length); + serv_addr.sin_port = htons(port); + + /* Now connect to the server */ + if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + perror("Error connecting"); + exit(1); + } + + return sockfd; +} From 3766dffbf64ea5d894ed6946c13efc2f202ab082 Mon Sep 17 00:00:00 2001 From: Konstantin Nedikov Date: Mon, 25 Mar 2019 19:26:17 +0300 Subject: [PATCH 3/3] change to udp --- udp/client/CMakeLists.txt | 2 +- udp/client/include/CalcuatorServerDriver.hpp | 14 ++-- udp/client/include/ConcurrentMap.hpp | 38 +++++++++++ udp/client/include/requests.hpp | 3 +- udp/client/include/socketUtils.hpp | 20 +++--- udp/client/src/CalcuatorServerDriver.cpp | 72 ++++++++++++-------- udp/client/src/CalculatorApp.cpp | 15 +++- udp/client/src/socketUtils.cpp | 67 ++++++------------ udp/server/CMakeLists.txt | 2 +- 9 files changed, 138 insertions(+), 95 deletions(-) create mode 100644 udp/client/include/ConcurrentMap.hpp diff --git a/udp/client/CMakeLists.txt b/udp/client/CMakeLists.txt index bae8c75..5f080c9 100644 --- a/udp/client/CMakeLists.txt +++ b/udp/client/CMakeLists.txt @@ -4,7 +4,7 @@ set(CMAKE_CXX_STANDARD 17) file(GLOB SOURCE_FILES "src/*.cpp") -add_executable(calculator ${SOURCE_FILES} src/CalculatorApp.cpp include/CalculatorApp.hpp src/CalcuatorServerDriver.cpp include/CalcuatorServerDriver.hpp src/main.cpp include/requests.hpp include/ConcurrentQueue.hpp src/requests.cpp) +add_executable(calculator ${SOURCE_FILES}) target_include_directories(calculator PRIVATE include) diff --git a/udp/client/include/CalcuatorServerDriver.hpp b/udp/client/include/CalcuatorServerDriver.hpp index a720266..c451052 100644 --- a/udp/client/include/CalcuatorServerDriver.hpp +++ b/udp/client/include/CalcuatorServerDriver.hpp @@ -6,13 +6,15 @@ #include #include #include +#include #include "requests.hpp" #include "socketUtils.hpp" #include "ConcurrentQueue.hpp" +#include "ConcurrentMap.hpp" class CalcuatorServerDriver { public: - CalcuatorServerDriver(std::string host, uint16_t port) : m_host(std::move(host)), m_port(port) {} + CalcuatorServerDriver(std::string host, uint16_t port) : m_host_str(std::move(host)), m_port(port) {} ~CalcuatorServerDriver(); @@ -34,17 +36,21 @@ class CalcuatorServerDriver { CalculatorResponse divide(uint32_t id, int64_t arg1, int64_t arg2); + void results(); + private: void sendRequest(CalculatorRequest const &request); - CalculatorResponse getResponse(); + CalculatorResponse getResponse(CalculatorRequest const &request); void readingThreadTask(); ConcurrentQueue m_longResults; - ConcurrentQueue m_instantResults; + ConcurrentMap m_instantResults; int m_socket = 0; - std::string m_host; + std::string m_host_str; + sockaddr_in m_host; uint16_t m_port; std::thread m_readingThread; + std::unordered_set m_longOperations; }; diff --git a/udp/client/include/ConcurrentMap.hpp b/udp/client/include/ConcurrentMap.hpp new file mode 100644 index 0000000..bfc36c9 --- /dev/null +++ b/udp/client/include/ConcurrentMap.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +template +class ConcurrentMap { +public: + void put(Key const &key, Value const &value) { + m_setMutex.lock(); + m_map[key] = value; + m_setMutex.unlock(); + } + + bool contains(Key const &key) { + m_setMutex.lock(); + + if (m_map.find(key) != m_map.end()) { + m_setMutex.unlock(); + return true; + } + + m_setMutex.unlock(); + return false; + } + + Value pop(Key const &key) { + m_setMutex.lock(); + auto result = m_map[key]; + m_map.erase(key); + m_setMutex.unlock(); + return result; + } + +private: + std::mutex m_setMutex; + std::unordered_map m_map; +}; diff --git a/udp/client/include/requests.hpp b/udp/client/include/requests.hpp index 075a99d..1f552ed 100644 --- a/udp/client/include/requests.hpp +++ b/udp/client/include/requests.hpp @@ -43,5 +43,6 @@ enum RequestType { MULT, DIV, SQRT, - FACT + FACT, + LONG_OP_RESULT }; diff --git a/udp/client/include/socketUtils.hpp b/udp/client/include/socketUtils.hpp index 03ed503..2f1e4dd 100644 --- a/udp/client/include/socketUtils.hpp +++ b/udp/client/include/socketUtils.hpp @@ -8,29 +8,27 @@ #include #include -void writeToSocket(int socketDescriptor, const void *buffer, size_t size); +void writeToSocket(sockaddr_in &host, int socket, uint8_t const *data, size_t size); -void readFromSocket(int socketDescriptor, uint8_t *buffer, size_t size); +void readFromSocket(sockaddr_in &host, int socket, uint8_t *data, size_t size); void printError(const std::string &s); -void error(const std::string &s); - void error(const std::string &type, const std::string &s); -int connectToServer(std::string const &hostname, uint16_t port); +int socketInit(); + +sockaddr_in hostInit(std::string const &hostname, uint16_t port); template -void writeObject(int socketDescriptor, const T &object) { - writeToSocket(socketDescriptor, (uint8_t const *) &object, sizeof(T)); +void writeObject(sockaddr_in &host, int socket, const T &object) { + writeToSocket(host, socket, (uint8_t const *) &object, sizeof(T)); } template -T readObject(int socketDescriptor) { +T readObject(sockaddr_in &host, int socket) { T object; - readFromSocket(socketDescriptor, (uint8_t *) &object, sizeof(T)); + readFromSocket(host, socket, (uint8_t *) &object, sizeof(T)); return object; } - -std::string read_until_zero(int *ptr, char *buffer, size_t buffer_size); diff --git a/udp/client/src/CalcuatorServerDriver.cpp b/udp/client/src/CalcuatorServerDriver.cpp index 2513718..af6de15 100644 --- a/udp/client/src/CalcuatorServerDriver.cpp +++ b/udp/client/src/CalcuatorServerDriver.cpp @@ -1,7 +1,3 @@ -// -// Created by karvozavr on 16/02/19. -// - #include "CalcuatorServerDriver.hpp" CalcuatorServerDriver::~CalcuatorServerDriver() { @@ -10,20 +6,18 @@ CalcuatorServerDriver::~CalcuatorServerDriver() { } void CalcuatorServerDriver::connect() { - m_socket = connectToServer(m_host, m_port); + m_socket = socketInit(); + m_host = hostInit(m_host_str, m_port); m_readingThread = std::thread(&CalcuatorServerDriver::readingThreadTask, this); } void CalcuatorServerDriver::readingThreadTask() { while (true) { - auto response = readObject(m_socket); - if (response.errorCode != WAIT_FOR_RESULT) { - if (response.operationType == SLOW) { - m_longResults.push(response); - } else if (response.operationType == FAST) { - m_instantResults.push(response); - } + auto response = readObject(m_host, m_socket); + if (response.errorCode != WAIT_FOR_RESULT && response.operationType == SLOW) { + m_longResults.push(response); } + m_instantResults.put(response.computationId, response); } } @@ -32,44 +26,68 @@ bool CalcuatorServerDriver::hasResult() { } CalculatorResponse CalcuatorServerDriver::getResult() { - return m_longResults.pop(); + auto response = m_longResults.pop(); + m_longOperations.erase(response.computationId); + return response; } void CalcuatorServerDriver::factorial(uint32_t id, int64_t arg) { - sendRequest({FACT, id, arg, 0}); + getResponse({FACT, id, arg, 0}); + m_longOperations.insert(id); } void CalcuatorServerDriver::sqrt(uint32_t id, int64_t arg) { - sendRequest({SQRT, id, arg, 0}); + getResponse({SQRT, id, arg, 0}); + m_longOperations.insert(id); } CalculatorResponse CalcuatorServerDriver::plus(uint32_t id, int64_t arg1, int64_t arg2) { - sendRequest({PLUS, id, arg1, arg2}); - return getResponse(); + return getResponse({PLUS, id, arg1, arg2}); } CalculatorResponse CalcuatorServerDriver::minus(uint32_t id, int64_t arg1, int64_t arg2) { - sendRequest({MINUS, id, arg1, arg2}); - return getResponse(); + return getResponse({MINUS, id, arg1, arg2}); } CalculatorResponse CalcuatorServerDriver::multiply(uint32_t id, int64_t arg1, int64_t arg2) { - sendRequest({MULT, id, arg1, arg2}); - return getResponse(); + return getResponse({MULT, id, arg1, arg2}); } CalculatorResponse CalcuatorServerDriver::divide(uint32_t id, int64_t arg1, int64_t arg2) { - sendRequest({DIV, id, arg1, arg2}); - return getResponse(); + return getResponse({DIV, id, arg1, arg2}); +} + +void CalcuatorServerDriver::results() { + for (uint32_t id : m_longOperations) { + getResponse({LONG_OP_RESULT, id, 0, 0}); + } } void CalcuatorServerDriver::sendRequest(CalculatorRequest const &request) { - writeObject(m_socket, request); + writeObject(m_host, m_socket, request); } -CalculatorResponse CalcuatorServerDriver::getResponse() { - while (m_instantResults.empty()) {} - return m_instantResults.pop(); +CalculatorResponse CalcuatorServerDriver::getResponse(CalculatorRequest const &request) { + sendRequest(request); + int i = 0; + int resend = 0; + + while (!m_instantResults.contains(request.computationId)) { + if (i == 10) { + if (resend == 5) { + error("get response", "Connection error."); + } + sendRequest(request); + resend++; + i = 0; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + i++; + } + + return m_instantResults.pop(request.computationId); } + diff --git a/udp/client/src/CalculatorApp.cpp b/udp/client/src/CalculatorApp.cpp index 6e4ab50..e28d1b3 100644 --- a/udp/client/src/CalculatorApp.cpp +++ b/udp/client/src/CalculatorApp.cpp @@ -24,12 +24,20 @@ void CalculatorApp::processInput(std::string &line) { std::vector results((std::istream_iterator(iss)), std::istream_iterator()); - if (results.size() == 2 || results.size() == 3 || results.empty()) { + if (results.size() <= 3) { if (results.size() == 2) { if (results[0] == "fact") { m_driver.factorial(m_currentComputation, static_cast(std::stoull(results[1]))); } else if (results[0] == "sqrt") { m_driver.sqrt(m_currentComputation, static_cast(std::stoull(results[1]))); + } else { + std::cout << "Syntax error." << std::endl; + } + } else if (results.size() == 1) { + if (results[0] == "rslt") { + m_driver.results(); + } else { + std::cout << "Syntax error." << std::endl; } } else if (results.size() == 3 && results[0].length() == 1) { CalculatorResponse response{}; @@ -85,7 +93,10 @@ void CalculatorApp::printResponse(const CalculatorResponse &response) { void CalculatorApp::printEntryMessage() { std::cout - << "Welcome!\n\tOnline calculator 1.0\n\n\tSupported operations: + - * / fact sqrt\n\tUse prefix notation (e.g + 2 3).\n" + << "Welcome!\n" + << "\tOnline calculator 1.0\n\n" + << "\tSupported operations: + - * / fact sqrt rslt quit\n" + << "\tUse prefix notation (e.g + 2 3).\n" << std::endl; } diff --git a/udp/client/src/socketUtils.cpp b/udp/client/src/socketUtils.cpp index 921cfe4..ab03fa7 100644 --- a/udp/client/src/socketUtils.cpp +++ b/udp/client/src/socketUtils.cpp @@ -1,27 +1,17 @@ #include "socketUtils.hpp" -void writeToSocket(int socket_descriptor, const void *buf, size_t size) { - ssize_t n = write(socket_descriptor, buf, size); - if (n <= 0) { +void writeToSocket(sockaddr_in &host, int socket, uint8_t const *data, size_t size) { + ssize_t n = sendto(socket, data, size, 0, (const sockaddr *) &host, sizeof(host)); + if (size != n) { error("write", "failed to write to socket!"); } - - if (n != size) { - error("write", "unexpected end of socket! (Probably server disconnected)"); - exit(0); - } } -void readFromSocket(int socket_descriptor, uint8_t *buf, size_t size) { - ssize_t n; - while((n = read(socket_descriptor, buf, size)) < size) { - if (n <= 0) { - error("read", "failed to read from socket!"); - exit(0); - } - - buf += n; - size -= n; +void readFromSocket(sockaddr_in &host, int socket, uint8_t *data, size_t size) { + socklen_t len = sizeof(host); + ssize_t n = recvfrom(socket, (void *) data, size, 0, (sockaddr *) &host, &len); + if (size != n) { + error("read", "failed to read from socket!"); } } @@ -29,52 +19,33 @@ void printError(const std::string &s) { std::cerr << s << std::endl; } -void error(const std::string &s) { - printError("Error: " + s); - exit(0); -} - void error(const std::string &type, const std::string &s) { printError("Error " + type + ": " + s); exit(0); } -std::string read_until_zero(int* ptr, char* buffer, size_t buffer_size) { - std::string dest; - while (*ptr < buffer_size && buffer[*ptr] != '\0') { - dest += buffer[*ptr]; - (*ptr)++; - } - (*ptr)++; - - return dest; -} - -int connectToServer(std::string const &hostname, uint16_t port) { - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - struct sockaddr_in serv_addr{}; - struct hostent *server; +int socketInit() { + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { - error("Error opening socket"); + error("Init socket", "Network problem."); } - server = gethostbyname(hostname.c_str()); + return sockfd; +} + +sockaddr_in hostInit(std::string const &hostname, uint16_t port) { + hostent *server = gethostbyname(hostname.c_str()); if (server == nullptr) { - error("Connection", "Error, no such host"); + error("Host init", "Incorrect host."); } + sockaddr_in serv_addr{}; bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; bcopy(server->h_addr, (char *) &serv_addr.sin_addr.s_addr, (size_t) server->h_length); serv_addr.sin_port = htons(port); - /* Now connect to the server */ - if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - perror("Error connecting"); - exit(1); - } - - return sockfd; + return serv_addr; } diff --git a/udp/server/CMakeLists.txt b/udp/server/CMakeLists.txt index 1f9047e..e932937 100644 --- a/udp/server/CMakeLists.txt +++ b/udp/server/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.10) project(server) set(CMAKE_CXX_STANDARD 14)