summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederico Linhares <fred@linhares.blue>2025-02-20 21:22:08 -0300
committerFrederico Linhares <fred@linhares.blue>2025-02-26 13:48:56 -0300
commite12977bdc287bc2cdd5502ce8c50df4dd6c3ec0b (patch)
tree40a7b9eee13db3458adf0e7efc384b8dead70522
parent736637680ac7b2cd0d0b878401a7e044fde0ee6a (diff)
feat Code for networking
-rw-r--r--src/blu_cat/net/client/client.cpp63
-rw-r--r--src/blu_cat/net/client/client.hpp50
-rw-r--r--src/blu_cat/net/common/connection.cpp136
-rw-r--r--src/blu_cat/net/common/connection.hpp72
-rw-r--r--src/blu_cat/net/common/connection_callback.hpp39
-rw-r--r--src/blu_cat/net/common/message.hpp40
-rw-r--r--src/blu_cat/net/common/message_callback.hpp40
-rw-r--r--src/blu_cat/net/common/tsqueue.hpp105
-rw-r--r--src/blu_cat/net/server/server.cpp101
-rw-r--r--src/blu_cat/net/server/server.hpp54
10 files changed, 700 insertions, 0 deletions
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 <iostream>
+
+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<uint8_t> &msg)
+{
+ std::vector<uint8_t> *buffered_msg =
+ new std::vector<uint8_t>(msg.size() + 8);
+
+ { // Create header
+ std::memcpy(buffered_msg->data(), &id, 4);
+
+ uint32_t size{static_cast<uint32_t>(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 <iostream>
+
+#define ASIO_STANDALONE
+#include <asio.hpp>
+#include <asio/ts/buffer.hpp>
+#include <asio/ts/internet.hpp>
+
+#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<Message> 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<uint8_t> &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 <memory>
+#include <vector>
+
+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 <cstdint>
+#include <vector>
+
+namespace BluCat::NET
+{
+
+struct MessageHeader
+{
+ uint32_t id;
+ uint32_t size;
+};
+
+struct Message
+{
+ MessageHeader header{};
+ std::vector<uint8_t> 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 <deque>
+
+namespace BluCat::NET
+{
+
+template<typename T>
+class TSQueue
+{
+protected:
+ std::mutex mut;
+ std::deque<T> deq_queue;
+
+public:
+ TSQueue() = default;
+ TSQueue(const TSQueue<T>&) = 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 <iostream>
+
+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<unsigned long> free_connection_slots;
+ std::vector<Connection*> 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 */