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
18 changes: 12 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
project(DeepPic CXX)

cmake_minimum_required(VERSION 3.6)
cmake_minimum_required(VERSION 3.20)
project(boost_asio_async_server)

set(CMAKE_CXX_STANDARD 20)

file(GLOB_RECURSE HEADER_FILES project/include/*.h)
file(GLOB_RECURSE SOURCE_FILES project/src/*.cpp)
find_package(Boost 1.40.0 REQUIRED system)
add_definitions(-DBOOST_LOG_DYN_LINK)
find_package(Boost COMPONENTS log log_setup REQUIRED)
find_package(nlohmann_json 3.2.0 REQUIRED)
link_directories(${Boost_LIBRARY_DIR})

include_directories(project/include ${Boost_INCLUDE_DIRS})
aux_source_directory(project/src SRC)
add_executable(boost_asio_async_server ${SRC})

add_executable(main ${HEADER_FILES} ${SOURCE_FILES})
target_link_libraries(boost_asio_async_server boost_thread pthread ${BOOST_LIBRARIES} nlohmann_json::nlohmann_json Boost::log Boost::log_setup)
93 changes: 93 additions & 0 deletions about_json_api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Всевозможные сценарии комманд

Клиент, чтобы расшарить документ:
```json
{
"target": "sharing_document"
}
```

Сервер в ответ:
```json
{
"status": "OK", // или "FAIL"
"target": "sharing_document" // для указания цели, на которую пришел ответ
"auth_token": "dummy_aboba",
"address": "127.0.0.1",
"port": 5555
}
```

<hr>

Клиент, чтобы подключиться к документу:
```json
{
"target": "auth",
"auth_token": "dummy_aboba",
"address": "127.0.0.1",
"port": 5555
}
```

Сервер в ответ:
```json
{
"status": "OK" // или FAIL
"target": "auth"
}
```

<hr>

Клиент, чтобы запросить после подключения копию документа
```json
{
"target": "get_document"
}
```
Сервер клиенту, у которого есть копия документа:
```json
{
"target": "get_document"
}
```
Клиент в ответ на запрос сервера на документ:
```json
{
"status": "OK", // или FAIL, тогда поле document не обязательно
"target": "get_document",
"document": "some document code"
}
```

Сервер изначальному клиенту, который запрашивал документ:
```json
{
"status": "OK", // или FAIL, тогда поле document не обязательно
"target": "get_document",
"document": "some document code"
}
```

<hr>

Клиент, чтобы расшарить команду:
```json
{
"target": "sharing_command",
"command": "some command code"
}
```

Сервер в ответ: решительное ничего

Сервер всем остальным клиентам, подключенным к документу:
```json
{
"target": "sharing_command",
"command": "some command code"
}
```

Клиенты в ответ после получения команды: решительное ничего
65 changes: 65 additions & 0 deletions project/ServerConnection/include/ServerConnection.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#pragma once

#include <boost/asio.hpp>
#include <functional>
#include <string>
#include <mutex>
#include <condition_variable>
#include <atomic>

#define BUFFER_LENGTH 1024
#define END_STR "\r\n"


/*
* Структура с колбеками, которые будут вызываться после успешного чтения и записи соответственно.
*/
struct ServerConnectionCallbacks {
std::function<void(std::string &&)> onReadCb;
std::function<void(const boost::system::error_code &err)> onWriteCb;
};

class ServerConnection {
public:
ServerConnection(std::string &&url, int port, ServerConnectionCallbacks &&callbacks);

/*
* Выполняет подключение к серверу и начинает бесконечный цикл чтения сообщений с сервера.
* После того как прочитали сообщение, вызовется onReadCb из ServerConnectionCallbacks.
*/
void start();

/**
* Пишет серверу какое-либо сообщение. После записи вызовет onWriteCb, переданный в структуре ServerConnectionCallbacks
* @param message само сообщение
*/
void write(std::string &&message);

private:
void connectionToServer();

void read();

void readHandler(boost::system::error_code &err, size_t bytes_transferred);

std::size_t checkEndOfRead(const boost::system::error_code &err, std::size_t bytes_transferred);

void writeHandler(boost::system::error_code &err);

void run_ioc();

std::string serverUrl_;
int serverPort_;

boost::asio::io_context service_;
boost::asio::ip::tcp::socket socket_;
ServerConnectionCallbacks callbacks_;

std::mutex writeMutex_;
std::condition_variable writeCv_;
std::atomic<bool> canWrite_ = true;

char readBuf_[BUFFER_LENGTH];
char sendBuf_[BUFFER_LENGTH];
};

80 changes: 80 additions & 0 deletions project/ServerConnection/src/ServerConnection.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include <iostream>

#include "../include/ServerConnection.h"

ServerConnection::ServerConnection(std::string &&url, int port, ServerConnectionCallbacks &&callbacks) : socket_(service_),
serverUrl_(std::move(url)),
serverPort_(port),
callbacks_(std::move(callbacks)) {
}

void ServerConnection::connectionToServer() {
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(serverUrl_), serverPort_);
socket_.connect(ep);
read();
}

void ServerConnection::read() {
boost::asio::async_read(socket_, boost::asio::buffer(readBuf_),
[this](boost::system::error_code err, std::size_t bytes_transferred) -> size_t {
return this->checkEndOfRead(err, bytes_transferred);
}, [this](boost::system::error_code err, std::size_t bytes_transferred) {
this->readHandler(err, bytes_transferred);
});
}

void ServerConnection::readHandler(boost::system::error_code &err, size_t bytes_transferred) {
if (err) {
return;
}

if (callbacks_.onReadCb) {
callbacks_.onReadCb(std::string(readBuf_, readBuf_ + bytes_transferred));
}

read();
}

void ServerConnection::run_ioc() {
service_.run();
}

void ServerConnection::start() {
connectionToServer();
run_ioc();
}

void ServerConnection::write(std::string &&message) {
std::unique_lock<std::mutex> ulock(writeMutex_);
writeCv_.wait(ulock, [this]() { return bool(canWrite_); });
canWrite_ = false;

for (int i = 0; i < message.length(); ++i) {
sendBuf_[i] = message[i];
}

boost::asio::async_write(socket_, boost::asio::buffer(sendBuf_, message.length()), [this](boost::system::error_code err) {
this->writeHandler(err);
});
}

void ServerConnection::writeHandler(boost::system::error_code &err) {
if (err) {
return;
}

if (callbacks_.onWriteCb) {
callbacks_.onWriteCb(err);
}

canWrite_ = true;
writeCv_.notify_one();
}

std::size_t ServerConnection::checkEndOfRead(const boost::system::error_code &err, std::size_t bytes_transferred) {
if (bytes_transferred > 0 &&
std::string(readBuf_ + bytes_transferred - 1 - std::strlen(END_STR), readBuf_ + bytes_transferred - 1) == std::string(END_STR)) {
return 0;
}
return 1;
}
101 changes: 101 additions & 0 deletions project/include/Command.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#pragma once

#include <nlohmann/json.hpp>

#include "Settings.h"
#include "SharedDocumentServer.h"
#include "Connection.h"

using json = nlohmann::json;

typedef enum {
SHARING_COMMAND = 0x01,
GET_DOCUMENT = 0x02,
CREATE_DOCUMENT = 0x04
} command_t;

class Command {
public:
Command(command_t type_command, std::vector<std::shared_ptr<Connection>> *connection = nullptr);

Command(const Command &) = delete;

Command &operator=(Command &) = delete;

virtual bool do_command(json &command, std::shared_ptr<Connection> author);

~Command();

protected:
Command() : letter_(nullptr) {}

private:
Command *letter_;
};

class GetDocument : public Command {
public:
virtual bool do_command(json &command, std::shared_ptr<Connection> author) override;

GetDocument(const GetDocument &) = delete;

GetDocument &operator=(GetDocument &) = delete;

~GetDocument() = default;

private:
friend class Command;

explicit GetDocument(std::vector<std::shared_ptr<Connection>> *connections);

void getDocumentFromClient(const std::shared_ptr<Connection>& author);

void handleGetDocumentFromClient(std::shared_ptr<Connection> &connection, const std::shared_ptr<Connection>& author);

void sendDocumentToNewClients(std::string &&document);

std::vector<std::shared_ptr<Connection>> clientsToGetDocument_;

std::vector<std::shared_ptr<Connection>> *connections_ = nullptr;
};

class SharingCommand : public Command {
public:
virtual bool do_command(json &command, std::shared_ptr<Connection> author) override;

SharingCommand(const SharingCommand &) = delete;

SharingCommand &operator=(SharingCommand &) = delete;

~SharingCommand() = default;

private:
friend class Command;

explicit SharingCommand(std::vector<std::shared_ptr<Connection>> *connections);

std::vector<std::shared_ptr<Connection>> *connections_ = nullptr;
};

class CreateNewDocumentCommand : public Command {
public:
virtual bool do_command(json &command, std::shared_ptr<Connection> author) override;

~CreateNewDocumentCommand();

private:
friend class Command;

CreateNewDocumentCommand() = default;

std::vector<std::shared_ptr<SharedDocumentServer>> sharedDocuments_;
};

class DocumentCommandBus {
public:
explicit DocumentCommandBus(std::vector<std::shared_ptr<Connection>> *connection);
bool do_command(std::string &&command, std::shared_ptr<Connection> author);
private:
Command sharingCommand_;
Command getDocumentCommand_;
};
22 changes: 22 additions & 0 deletions project/include/CommandConstructor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include <nlohmann/json.hpp>

using json = nlohmann::json;

class CommandConstructor {
public:
static json sharingDocumentClient();

static json sharingDocumentServer(std::string &&status, std::string &&auth_token, std::string &&address, int port);

static json authClient(std::string &&auth_token);

static json authServer(std::string &&status, std::string &&token, std::string &&address, int port);

static json getDocumentClient();

static json getDocumentServer(std::string &&status, std::string &&document);

static json sharingCommandClient(std::string &&command_);
};
Loading