-
Notifications
You must be signed in to change notification settings - Fork 26
Зубаков Алексей. Lab1 client #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ottergottaott
wants to merge
1
commit into
h31:master
Choose a base branch
from
ottergottaott:task2
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| cmake_minimum_required(VERSION 3.12) | ||
| project(networks-labs) | ||
|
|
||
| set(CMAKE_CXX_STANDARD 17) | ||
|
|
||
| #add_subdirectory(CalculatorServer) | ||
| add_subdirectory(CalculatorClient) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| #include <netinet/in.h> | ||
| #include <netdb.h> | ||
| #include <cstdio> | ||
| #include <cstdlib> | ||
| #include <string.h> | ||
| #include <string> | ||
| #include <memory> | ||
| #include "operations.hpp" | ||
| #include "protocol.hpp" | ||
| #include <sys/socket.h> | ||
| #include <iostream> | ||
|
|
||
| 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<protocol::Ok *>(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; | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| #include <iostream> | ||
| #include <sstream> | ||
| #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; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| #include <cstdint> | ||
| #include <cmath> | ||
| #include <unistd.h> | ||
| #include <cstdio> | ||
| #include <string> | ||
|
|
||
| #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(""); | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| #include "operations.hpp" | ||
| #include <memory> | ||
| #include <string> | ||
| #include <cstring> | ||
|
|
||
| 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<uint8_t >(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<char[]> 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<Type>(operationType)); | ||
| } | ||
|
|
||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Не понимаю логики работы. Если, например,
readбудет возвращать 0 или -1, то мы будем бесконечно вращаться в цикле.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Будем врощаться до тех пор, пока не придет ответ, что кажется мне разумным.
Если Вам не сложно, объясните, пожалуйста, как Вы предлагаете обрабатывать ситуацию, когда
readвернет 0 или -1.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
readравный 0 - соединение закрыто удаленной стороной, нужно закрывать клиент.readравный -1 - ошибка, выводим на экран сообщение и тоже выходим. Главное, как-то реагировать и выходить из цикла, потому что повторный вызов вернет тот же результат, и цикл продолжится.Ответ может прийти двумя частями. Т.е. не сразу 5 байт, а первый раз
ssize = 3, а второй -ssize = 2. В обоих случаях цикл будет продолжать работать и считывать новую порцию данных. А потом сокет заблокируется, потому что он сейчас работает в блокирующем режиме. А потом когда-нибудь сервер отключится, клиент зависнет и будет потреблять 100% процессорного времени. Самый простой способ исправить это - написатьssize += read(...);, плюс обработать вариант сssize <= 0.