diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f0ebdb7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.12) +project(networks-labs) + +set(CMAKE_CXX_STANDARD 17) + +#add_subdirectory(CalculatorServer) +add_subdirectory(CalculatorClient) \ No newline at end of file diff --git a/CalculatorClient/CMakeLists.txt b/CalculatorClient/CMakeLists.txt new file mode 100644 index 0000000..010a53b --- /dev/null +++ b/CalculatorClient/CMakeLists.txt @@ -0,0 +1,11 @@ +project(Calculator) + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + +add_executable(${PROJECT_NAME} src/main.cpp src/calc_client.hpp src/protocol.hpp src/operations.hpp) + +#target_link_libraries(${PROJECT_NAME} EmailCommon ${TBB_IMPORTED_TARGETS}) + +target_include_directories(${PROJECT_NAME} + PUBLIC ${PROJECT_SOURCE_DIR}/include + ) \ No newline at end of file diff --git a/CalculatorClient/src/calc_client.hpp b/CalculatorClient/src/calc_client.hpp new file mode 100644 index 0000000..00dfa52 --- /dev/null +++ b/CalculatorClient/src/calc_client.hpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#include +#include "operations.hpp" +#include "protocol.hpp" +#include +#include + +using std::string; + + +using namespace operations; + +struct CalcClient { + + + CalcClient(const char *hostname, uint16_t port) { + sockaddr_in serv_addr; + hostent *server; + + sock_desc = socket(AF_INET, SOCK_STREAM, 0); + + if (sock_desc < 0) { + perror("ERROR opening socket"); + exit(1); + } + + server = gethostbyname(hostname); + + if (server == nullptr) { + fprintf(stderr, "ERROR, no such host\n"); + exit(0); + } + + 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); + + if (connect(sock_desc, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + perror("ERROR connecting"); + exit(1); + } + } + + void execute(const string &type, int32_t arg1, int32_t arg2) { + Type req = operations::typeFromString(type); + protocol::Response *response = doRequest(req, arg1, arg2); + + if (response->getStatus() == protocol::status::Ok) { + std::cout << "Evaluation result: " << (static_cast(response))->getResult(); + } else { + std::cout << "Wrong result :c" << std::endl; + } + + delete response; + } + +private: + protocol::Response *doRequest(operations::Type type, int32_t arg1, int32_t arg2) { + char *buffer = new char[protocol::REQUEST_SIZE]; + switch (type) { + case Fact: + case Sq: { + protocol::LongOperationRequest(arg1, arg2, type).toCharArray(buffer); + }; + + case Add: + case Subs: + case Div: + case Mult: { + protocol::QuickOperationRequest(arg1, arg2, type).toCharArray(buffer); + } + ssize_t i = -1; + while (i < protocol::REQUEST_SIZE) { + i = send(sock_desc, buffer + i, protocol::REQUEST_SIZE - i, 0); + } + return protocol::parseResult(sock_desc); + } + } + + +private: + int sock_desc; +}; \ No newline at end of file diff --git a/CalculatorClient/src/main.cpp b/CalculatorClient/src/main.cpp new file mode 100644 index 0000000..f61fde3 --- /dev/null +++ b/CalculatorClient/src/main.cpp @@ -0,0 +1,60 @@ +#include +#include +#include "calc_client.hpp" + + +int main() { + std::cout << "Hello in calculator client!" << std::endl; + + string host; + + std::cout << "Enter server address" << std::endl; + getline(std::cin, host); + + + uint16_t port; + std::cout << "Enter server port" << std::endl; + + std::string input; + getline(std::cin, input); + if (!input.empty()) { + std::istringstream stream(input); + stream >> port; + } + + CalcClient client(host.c_str(), port); + + while (true) { + + string command; + + int32_t arg1, arg2; + + std::cout << "Enter one of comands: fact, square, sub, add, mult, div, exit " << std::endl; + + getline(std::cin, command); + if (command == "exit") { + std::cout << "Exit."; + break; + } + + std::string arg_input; + std::cout << "Enter first arg" << std::endl; + getline(std::cin, arg_input); + if (!arg_input.empty()) { + std::istringstream stream(arg_input); + stream >> arg1; + } + + std::cout << "Enter second arg" << std::endl; + getline(std::cin, arg_input); + if (!arg_input.empty()) { + std::istringstream stream(arg_input); + stream >> arg2; + } + + client.execute(command, arg1, arg2); + } + + return 0; +} \ No newline at end of file diff --git a/CalculatorClient/src/operations.hpp b/CalculatorClient/src/operations.hpp new file mode 100644 index 0000000..252b4a8 --- /dev/null +++ b/CalculatorClient/src/operations.hpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +#pragma once +namespace operations { + using std::string; + enum Type { + Fact = 1, + Sq = 2, + Add = 3, + Subs = 4, + Mult = 5, + Div = 6 + }; + + + Type typeFromString(const string &text) { + if (text == "fact") { + return Fact; + } + + if (text == "sq") { + return Sq; + } + + if (text == "add") { + return Add; + } + + if (text == "sub") { + return Subs; + } + + if (text == "mul") { + return Mult; + } + + if (text == "div") { + return Div; + } + + perror("Invalid operation type"); + }; + + string stringFromType(Type type) { + switch (type) { + case Type::Fact : + return "factorial"; + case Type::Sq: + return "square"; + case Type::Add: + return "addition"; + case Subs: + return "substract"; + case Mult: + return "multiplication"; + case Div: + return "division"; + default: + perror(""); + } + } + +} + + diff --git a/CalculatorClient/src/protocol.hpp b/CalculatorClient/src/protocol.hpp new file mode 100644 index 0000000..89385ca --- /dev/null +++ b/CalculatorClient/src/protocol.hpp @@ -0,0 +1,142 @@ +#include "operations.hpp" +#include +#include +#include + +namespace protocol { + // result -- 4 bytes + // opertaion type -- 1 byte + // status -- 1 byte + // or 1 byte for status + const size_t MAX_RESPONSE_SIZE = 4 + 1 + 1; + + // arg1 -- 4 bytes + // arg2 or executation time -- 4 bytes + // opertation type -- 1 byte + const size_t REQUEST_SIZE = 4 + 4 + 1; + + struct Request { + explicit Request(operations::Type type) : type_(type) {} + + virtual void toCharArray(char *buffer) = 0; + + protected: + const operations::Type type_; + + void writeIntToCharArray(char *buffer, int32_t value, size_t startPosition) { + const size_t end = startPosition + 4; + for (size_t i = startPosition; i < end; ++i) { + buffer[i] = static_cast(value & 0xffu); + value >>= 8; + } + } + }; + + struct LongOperationRequest : Request { + LongOperationRequest(int32_t param, int32_t time, operations::Type type) : + param_(param), + time_(time), + Request(type) {} + + + void toCharArray(char *buffer) override { + writeIntToCharArray(buffer, param_, 0); + writeIntToCharArray(buffer, time_, 0); + buffer[REQUEST_SIZE - 2] = type_; + } + + private: + const int32_t param_; + const int32_t time_; + + }; + + struct QuickOperationRequest : Request { + QuickOperationRequest(int32_t param1, int32_t param2, operations::Type type) : + param1_(param1), + param2_(param2), + Request(type) {} + + void toCharArray(char *buffer) override { + writeIntToCharArray(buffer, param1_, 0); + writeIntToCharArray(buffer, param2_, 0); + buffer[REQUEST_SIZE - 2] = type_; + } + + private: + const int32_t param1_; + const int32_t param2_; + + + }; + + + int32_t parseInt(const char *buffer, const size_t start) { + int32_t result = buffer[start + 4 - 1]; + for (size_t i = start + 4 - 1; i > start; --i) { + result <<= 8; + result += buffer[i - 1]; + } + return result; + } + + + using namespace operations; + + namespace status { + enum Status { + Ok = 0, + Fail = -1 + }; + + } + + struct Response { + explicit Response(status::Status stat) : stat_(stat) {}; + + status::Status getStatus() { + return stat_; + } + + private: + const status::Status stat_; + }; + + struct Ok : public Response { + Ok(int32_t val, operations::Type type) : result_(val), type_(type), Response(status::Ok) { + + } + + int32_t getResult() { + return result_; + } + + private: + int32_t result_; + operations::Type type_; + }; + + struct Fail : public Response { + Fail() : + Response(status::Fail) {}; + }; + + Response *parseResult(const int socket) { + std::unique_ptr buffer(new char[MAX_RESPONSE_SIZE]); + + ssize_t ssize = 0; + while (ssize < MAX_RESPONSE_SIZE) { + if (ssize == 1 && buffer[0] == status::Fail) { + return new Fail(); + } + + ssize = read(socket, buffer.get() + ssize, MAX_RESPONSE_SIZE - ssize); + } + + int32_t result = parseInt(buffer.get(), 0); + char &operationType = buffer[MAX_RESPONSE_SIZE - 2]; + + return new Ok(result, static_cast(operationType)); + } + +} \ No newline at end of file