From e12977bdc287bc2cdd5502ce8c50df4dd6c3ec0b Mon Sep 17 00:00:00 2001 From: Frederico Linhares Date: Thu, 20 Feb 2025 21:22:08 -0300 Subject: feat Code for networking --- src/blu_cat/net/client/client.cpp | 63 ++++++++++++ src/blu_cat/net/client/client.hpp | 50 +++++++++ src/blu_cat/net/common/connection.cpp | 136 +++++++++++++++++++++++++ src/blu_cat/net/common/connection.hpp | 72 +++++++++++++ src/blu_cat/net/common/connection_callback.hpp | 39 +++++++ src/blu_cat/net/common/message.hpp | 40 ++++++++ src/blu_cat/net/common/message_callback.hpp | 40 ++++++++ src/blu_cat/net/common/tsqueue.hpp | 105 +++++++++++++++++++ src/blu_cat/net/server/server.cpp | 101 ++++++++++++++++++ src/blu_cat/net/server/server.hpp | 54 ++++++++++ 10 files changed, 700 insertions(+) create mode 100644 src/blu_cat/net/client/client.cpp create mode 100644 src/blu_cat/net/client/client.hpp create mode 100644 src/blu_cat/net/common/connection.cpp create mode 100644 src/blu_cat/net/common/connection.hpp create mode 100644 src/blu_cat/net/common/connection_callback.hpp create mode 100644 src/blu_cat/net/common/message.hpp create mode 100644 src/blu_cat/net/common/message_callback.hpp create mode 100644 src/blu_cat/net/common/tsqueue.hpp create mode 100644 src/blu_cat/net/server/server.cpp create mode 100644 src/blu_cat/net/server/server.hpp diff --git a/src/blu_cat/net/client/client.cpp b/src/blu_cat/net/client/client.cpp new file mode 100644 index 0000000..13601e5 --- /dev/null +++ b/src/blu_cat/net/client/client.cpp @@ -0,0 +1,63 @@ +/* + * Copyright 2022-2025 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "client.hpp" + +namespace BluCat::NET +{ + +void +Client::read_messages() +{ + std::scoped_lock lock(this->mut); + if(this->connection) this->connection->read_messages(); +} + +void +Client::end_connection(unsigned long index) +{ + std::scoped_lock lock(this->mut); + + delete this->connection; + this->connection = nullptr; +} + +Client::Client( + MessageCallback *(*callback_instantiator)(), const char *host, + const uint16_t port): + callback_instantiator{callback_instantiator} +{ + asio::error_code error; + asio::ip::tcp::endpoint endpoint{asio::ip::make_address(host, error), port}; + + asio::ip::tcp::socket socket(this->io_context); + socket.connect(endpoint); + + this->connection = new Connection( + this->callback_instantiator(), this, this->io_context, std::move(socket), + 0); + + this->thread_context = std::thread([this](){this->io_context.run();}); +} + +Client::~Client() +{ + this->io_context.stop(); + if(this->thread_context.joinable()) this->thread_context.join(); + if(this->connection) delete this->connection; +} + +} diff --git a/src/blu_cat/net/client/client.hpp b/src/blu_cat/net/client/client.hpp new file mode 100644 index 0000000..60190c8 --- /dev/null +++ b/src/blu_cat/net/client/client.hpp @@ -0,0 +1,50 @@ +/* + * Copyright 2022-2025 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BLU_CAT_NET_CLIENT_CLIENT_H +#define BLU_CAT_NET_CLIENT_CLIENT_H 1 + +#include "../common/connection.hpp" +#include "../common/connection_callback.hpp" + +namespace BluCat::NET +{ + +class Client: public ConnectionCallback +{ + std::mutex mut; + asio::io_context io_context; + std::thread thread_context; + MessageCallback *(*callback_instantiator)(); + + Connection *connection; + +public: + void + read_messages(); + + void + end_connection(unsigned long index); + + Client( + MessageCallback *(*callback_instantiator)(), const char *host, + const uint16_t port); + ~Client(); +}; + +} + +#endif /* BLU_CAT_NET_CLIENT_CLIENT_H */ diff --git a/src/blu_cat/net/common/connection.cpp b/src/blu_cat/net/common/connection.cpp new file mode 100644 index 0000000..b4e8f1b --- /dev/null +++ b/src/blu_cat/net/common/connection.cpp @@ -0,0 +1,136 @@ +/* + * Copyright 2022-2025 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "connection.hpp" + +#include + +namespace BluCat::NET +{ + +void +Connection::read_header() +{ + asio::async_read( + this->socket, asio::buffer( + &this->reading_message.header, sizeof(MessageHeader)), + [this](std::error_code error, std::size_t length) + { + if(!error) + { + if(this->reading_message.header.size > 0) + { + this->reading_message.body.resize(this->reading_message.header.size); + this->read_body(); + } + else + { + this->reading_message.body.resize(0); + this->messages.push_back(this->reading_message); + this->read_header(); + } + } + else + { + std::cout << "Failed to read header: " << error.message() << std::endl; + this->connection_callback->end_connection(this->index); + } + }); +} + +void +Connection::read_body() +{ + asio::async_read( + this->socket, asio::buffer( + this->reading_message.body.data(), this->reading_message.body.size()), + [this](std::error_code error, std::size_t length) + { + if(!error) + { + this->messages.push_back(this->reading_message); + this->read_header(); + } + else + { + std::cout << "Failed to read body." << std::endl; + this->connection_callback->end_connection(this->index); + } + }); +} + +bool +Connection::send(const uint32_t id, const std::vector &msg) +{ + std::vector *buffered_msg = + new std::vector(msg.size() + 8); + + { // Create header + std::memcpy(buffered_msg->data(), &id, 4); + + uint32_t size{static_cast(msg.size())}; + std::memcpy(buffered_msg->data() + 4, &size, 4); + } + + // Append string to buffer. + copy(msg.begin(), msg.end(), buffered_msg->begin() + 8); + + asio::async_write( + this->socket, asio::buffer(buffered_msg->data(), buffered_msg->size()), + [this, buffered_msg](std::error_code error, std::size_t length) + { + if(error) + { + std::cout << "Failed to send message: " << error.message() << + std::endl; + this->connection_callback->end_connection(this->index); + } + delete buffered_msg; + }); + + return true; +} + +void +Connection::read_messages() +{ + while(this->messages.size() > 0) + this->message_callback->read_message( + std::move(this->messages.pop_back())); +} + +Connection::Connection( + MessageCallback *message_callback, + ConnectionCallback *connection_callback, + asio::io_context &io_context, asio::ip::tcp::socket socket, + unsigned long index): + message_callback{message_callback}, + connection_callback{connection_callback}, + io_context{io_context}, + socket{std::move(socket)}, + index{index} +{ + this->read_header(); +} + +Connection::~Connection() +{ + this->message_callback->disconnect(); + delete message_callback; + this->socket.close(); +} + +} diff --git a/src/blu_cat/net/common/connection.hpp b/src/blu_cat/net/common/connection.hpp new file mode 100644 index 0000000..c5683cb --- /dev/null +++ b/src/blu_cat/net/common/connection.hpp @@ -0,0 +1,72 @@ +/* + * Copyright 2022-2025 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BLU_CAT_NET_COMMON_CONNECTION_H +#define BLU_CAT_NET_COMMON_CONNECTION_H 1 + +#include + +#define ASIO_STANDALONE +#include +#include +#include + +#include "connection_callback.hpp" +#include "message.hpp" +#include "message_callback.hpp" +#include "tsqueue.hpp" + +namespace BluCat::NET +{ + +class Connection +{ +protected: + unsigned long index; + asio::io_context &io_context; + + asio::ip::tcp::socket socket; + TSQueue messages; + + Message reading_message; + MessageCallback *message_callback; + ConnectionCallback *connection_callback; + + void + read_header(); + + void + read_body(); + +public: + bool + send(const uint32_t id, const std::vector &msg); + + void + read_messages(); + + Connection(MessageCallback *message_callback, + ConnectionCallback *connection_callback, + asio::io_context &io_context, asio::ip::tcp::socket socket, + unsigned long index); + + virtual + ~Connection(); +}; + +} + +#endif /* BLU_CAT_NET_COMMON_CONNECTION_H */ diff --git a/src/blu_cat/net/common/connection_callback.hpp b/src/blu_cat/net/common/connection_callback.hpp new file mode 100644 index 0000000..815d393 --- /dev/null +++ b/src/blu_cat/net/common/connection_callback.hpp @@ -0,0 +1,39 @@ +/* + * Copyright 2022-2025 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BLU_CAT_NET_COMMON_CONNECTION_CALLBACK_H +#define BLU_CAT_NET_COMMON_CONNECTION_CALLBACK_H 1 + +#include +#include + +namespace BluCat::NET +{ + +class Connection; + +struct ConnectionCallback +{ + virtual void + end_connection(unsigned long index)=0; + + virtual + ~ConnectionCallback(){}; +}; + +} + +#endif /* BLU_CAT_NET_COMMON_CONNECTION_CALLBACK_H */ diff --git a/src/blu_cat/net/common/message.hpp b/src/blu_cat/net/common/message.hpp new file mode 100644 index 0000000..286a3bc --- /dev/null +++ b/src/blu_cat/net/common/message.hpp @@ -0,0 +1,40 @@ +/* + * Copyright 2022-2025 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BLU_CAT_NET_COMMON_MESSAGE_H +#define BLU_CAT_NET_COMMON_MESSAGE_H 1 + +#include +#include + +namespace BluCat::NET +{ + +struct MessageHeader +{ + uint32_t id; + uint32_t size; +}; + +struct Message +{ + MessageHeader header{}; + std::vector body; +}; + +} + +#endif /* BLU_CAT_NET_COMMON_MESSAGE_H */ diff --git a/src/blu_cat/net/common/message_callback.hpp b/src/blu_cat/net/common/message_callback.hpp new file mode 100644 index 0000000..b17886c --- /dev/null +++ b/src/blu_cat/net/common/message_callback.hpp @@ -0,0 +1,40 @@ +/* + * Copyright 2022-2025 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BLU_CAT_NET_COMMON_MESSAGE_CALLBACK_H +#define BLU_CAT_NET_COMMON_MESSAGE_CALLBACK_H 1 + +#include "message.hpp" + +namespace BluCat::NET +{ + +struct MessageCallback +{ + virtual void + read_message(Message m)=0; + + virtual void + disconnect()=0; + + virtual + ~MessageCallback(){}; +}; + +} + + +#endif /* BLU_CAT_NET_COMMON_MESSAGE_CALLBACK_H */ diff --git a/src/blu_cat/net/common/tsqueue.hpp b/src/blu_cat/net/common/tsqueue.hpp new file mode 100644 index 0000000..4cdd21d --- /dev/null +++ b/src/blu_cat/net/common/tsqueue.hpp @@ -0,0 +1,105 @@ +/* + * Copyright 2022-2025 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BLU_CAT_NET_COMMON_TSQUEUE_H +#define BLU_CAT_NET_COMMON_TSQUEUE_H 1 + +#include + +namespace BluCat::NET +{ + +template +class TSQueue +{ +protected: + std::mutex mut; + std::deque deq_queue; + +public: + TSQueue() = default; + TSQueue(const TSQueue&) = delete; + + const T& + front() + { + std::scoped_lock lock(this->mut); + return this->deq_queue.front(); + } + + const T& + back() + { + std::scoped_lock lock(this->mut); + return this->deq_queue.back(); + } + + void + push_front(const T& item) + { + std::scoped_lock lock(this->mut); + this->deq_queue.emplace_front(std::move(item)); + } + + void + push_back(const T& item) + { + std::scoped_lock lock(this->mut); + this->deq_queue.emplace_back(std::move(item)); + } + + size_t + size() + { + std::scoped_lock lock(this->mut); + return this->deq_queue.size(); + } + + void + clear() + { + std::scoped_lock lock(this->mut); + return this->deq_queue.clear(); + } + + T + pop_front() + { + std::scoped_lock lock(this->mut); + auto t = std::move(this->deq_queue.front()); + deq_queue.pop_front(); + return t; + } + + T + pop_back() + { + std::scoped_lock lock(this->mut); + auto t = std::move(this->deq_queue.back()); + deq_queue.pop_back(); + return t; + } + + virtual + ~TSQueue() + { + this->clear(); + } +}; + +} + +#endif /* BLU_CAT_NET_COMMON_TSQUEUE_H */ diff --git a/src/blu_cat/net/server/server.cpp b/src/blu_cat/net/server/server.cpp new file mode 100644 index 0000000..575e203 --- /dev/null +++ b/src/blu_cat/net/server/server.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2022-2025 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "server.hpp" + +#include + +namespace BluCat::NET +{ + +void +Server::wait_for_client_connections() +{ + acceptor.async_accept( + [this](std::error_code error, asio::ip::tcp::socket socket) + { + if(!error) + { + std::scoped_lock lock(this->mut); + + std::cout << "[SERVER] New connection: " << socket.remote_endpoint() << + std::endl; + + if(this->free_connection_slots.size() > 0) + { + unsigned long pos{this->free_connection_slots.pop_front()}; + std::cout << "Working " << pos << std::endl; + Connection *new_connection = new Connection( + this->callback_instantiator(), this, this->io_context, + std::move(socket), pos); + this->connections[pos] = new_connection; + } + else + { + unsigned long pos{this->connections.size()}; + Connection *new_connection = new Connection( + this->callback_instantiator(), this, this->io_context, + std::move(socket), pos); + this->connections.push_back(new_connection); + } + } + else + { + std::cout << "error connecting to client." << std::endl; + } + + std::cout << "num clients: " << connections.size() << std::endl; + + this->wait_for_client_connections(); + }); +} + +void +Server::read_messages() +{ + std::scoped_lock lock(this->mut); + for(auto i{0}; i < this->connections.size(); i++) + { + Connection *c{this->connections[i]}; + if(c != nullptr) c->read_messages(); + } +} + +void +Server::end_connection(unsigned long index) +{ + std::scoped_lock lock(this->mut); + delete this->connections[index]; + this->connections[index] = nullptr; + this->free_connection_slots.push_back(index); +} + +Server::Server(MessageCallback *(*callback_instantiator)(), const uint16_t port): + callback_instantiator{callback_instantiator}, + acceptor{io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)} +{ + this->thread_context = std::thread([this](){this->io_context.run();}); + this->wait_for_client_connections(); +} + +Server::~Server() +{ + this->connections.clear(); + this->io_context.stop(); + if(this->thread_context.joinable()) thread_context.join(); +} + +} diff --git a/src/blu_cat/net/server/server.hpp b/src/blu_cat/net/server/server.hpp new file mode 100644 index 0000000..271d75a --- /dev/null +++ b/src/blu_cat/net/server/server.hpp @@ -0,0 +1,54 @@ +/* + * Copyright 2022-2025 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BLU_CAT_SERVER_SERVER_H +#define BLU_CAT_SERVER_SERVER_H 1 + +#include "../common/connection.hpp" +#include "../common/connection_callback.hpp" +#include "../common/tsqueue.hpp" + +namespace BluCat::NET +{ + +class Server: public ConnectionCallback +{ + std::mutex mut; + asio::io_context io_context; + std::thread thread_context; + asio::ip::tcp::acceptor acceptor; + MessageCallback *(*callback_instantiator)(); + + TSQueue free_connection_slots; + std::vector connections; + + void + wait_for_client_connections(); + +public: + void + read_messages(); + + void + end_connection(unsigned long index); + + Server(MessageCallback *(*callback_instantiator)(), const uint16_t port); + ~Server(); +}; + +} + +#endif /* BLU_CAT_SERVER_SERVER_H */ -- cgit v1.2.3