Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CMakeLists.txt
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)
11 changes: 11 additions & 0 deletions CalculatorClient/CMakeLists.txt
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
)
89 changes: 89 additions & 0 deletions CalculatorClient/src/calc_client.hpp
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;
};
60 changes: 60 additions & 0 deletions CalculatorClient/src/main.cpp
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;
}
69 changes: 69 additions & 0 deletions CalculatorClient/src/operations.hpp
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("");
}
}

}


142 changes: 142 additions & 0 deletions CalculatorClient/src/protocol.hpp
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) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не понимаю логики работы. Если, например, read будет возвращать 0 или -1, то мы будем бесконечно вращаться в цикле.

Copy link
Author

@ottergottaott ottergottaott Mar 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Если Вам не сложно, объясните, пожалуйста, как Вы предлагаете обрабатывать ситуацию, когда read вернет 0 или -1.

Copy link
Owner

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.

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

}