aboutsummaryrefslogtreecommitdiff
path: root/libcmix-network
diff options
context:
space:
mode:
Diffstat (limited to 'libcmix-network')
-rw-r--r--libcmix-network/CMakeLists.txt29
-rw-r--r--libcmix-network/acceptor.cpp59
-rw-r--r--libcmix-network/acceptor.hpp18
-rw-r--r--libcmix-network/client.cpp41
-rw-r--r--libcmix-network/client.hpp24
-rw-r--r--libcmix-network/nodeclient.cpp22
-rw-r--r--libcmix-network/nodeclient.hpp20
-rw-r--r--libcmix-network/server.cpp31
-rw-r--r--libcmix-network/server.hpp29
9 files changed, 273 insertions, 0 deletions
diff --git a/libcmix-network/CMakeLists.txt b/libcmix-network/CMakeLists.txt
new file mode 100644
index 0000000..d819bec
--- /dev/null
+++ b/libcmix-network/CMakeLists.txt
@@ -0,0 +1,29 @@
+find_package(Boost COMPONENTS system REQUIRED)
+
+add_library(cmix-network
+ acceptor.hpp acceptor.cpp
+ server.hpp server.cpp
+ client.hpp client.cpp
+ nodeclient.hpp nodeclient.cpp
+)
+
+target_compile_options(cmix-network
+ PRIVATE "-std=c++11"
+)
+
+target_include_directories(cmix-network
+ PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+target_link_libraries(cmix-network
+ PRIVATE Boost::boost
+ PUBLIC Boost::system
+ PRIVATE cmix
+)
+
+if(WIN32)
+ target_link_libraries(cmix-network
+ PUBLIC wsock32
+ PUBLIC ws2_32
+ )
+endif()
diff --git a/libcmix-network/acceptor.cpp b/libcmix-network/acceptor.cpp
new file mode 100644
index 0000000..34b5f0e
--- /dev/null
+++ b/libcmix-network/acceptor.cpp
@@ -0,0 +1,59 @@
+#include "acceptor.hpp"
+
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/ip/v6_only.hpp>
+#include <boost/asio/placeholders.hpp>
+#include <boost/bind.hpp>
+
+
+using namespace boost::asio;
+using namespace boost::asio::ip;
+
+void accept_loop(tcp::acceptor& acceptor, std::function<void(tcp::socket&&)> f);
+
+void accept_connection(tcp::acceptor& acceptor, std::shared_ptr<tcp::socket> socket, boost::system::error_code ec, std::function<void(boost::asio::ip::tcp::socket&&)> f)
+{
+ if(!bool(ec))
+ {
+ f(std::move(*socket));
+ accept_loop(acceptor, f);
+ } else {
+ std::stringstream ss;
+ ss << ec;
+ throw std::runtime_error(ss.str());
+ }
+
+}
+
+void accept_loop(tcp::acceptor& acceptor, std::function<void(tcp::socket&&)> f)
+{
+ std::shared_ptr<tcp::socket> new_socket = std::make_shared<tcp::socket>(acceptor.get_io_service());
+
+ acceptor.async_accept(*new_socket, boost::bind(accept_connection, boost::ref(acceptor), new_socket, boost::asio::placeholders::error, f));
+}
+
+Acceptor::Acceptor(boost::asio::io_service &io_service, boost::asio::ip::address address, uint16_t port)
+: acceptor(io_service)
+, endpoint(address, port)
+{}
+
+void Acceptor::bind_v6_and_v4_any(std::function<void(tcp::socket&&)> accept_handler) {
+ acceptor.open(endpoint.protocol());
+
+ v6_only option(false);
+ acceptor.set_option(option);
+
+ acceptor.bind(endpoint);
+ acceptor.listen();
+
+ accept_loop(acceptor, accept_handler);
+}
+
+void Acceptor::setup_listen_socket(std::function<void (tcp::socket &&)> accept_handler)
+{
+ acceptor.open(endpoint.protocol());
+ acceptor.bind(endpoint);
+ acceptor.listen();
+
+ accept_loop(acceptor, accept_handler);
+}
diff --git a/libcmix-network/acceptor.hpp b/libcmix-network/acceptor.hpp
new file mode 100644
index 0000000..7d8e0a2
--- /dev/null
+++ b/libcmix-network/acceptor.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <boost/asio/ip/tcp.hpp>
+
+#include <functional>
+
+class Acceptor{
+ boost::asio::ip::tcp::acceptor acceptor;
+ boost::asio::ip::tcp::endpoint endpoint;
+
+public:
+ Acceptor(boost::asio::io_service& io_service, boost::asio::ip::address address, uint16_t port);
+
+ boost::asio::ip::address get_address() { return endpoint.address(); }
+
+ void bind_v6_and_v4_any(std::function<void(boost::asio::ip::tcp::socket&&)> accept_handler);
+ void setup_listen_socket(std::function<void(boost::asio::ip::tcp::socket&&)> accept_handler);
+};
diff --git a/libcmix-network/client.cpp b/libcmix-network/client.cpp
new file mode 100644
index 0000000..3098185
--- /dev/null
+++ b/libcmix-network/client.cpp
@@ -0,0 +1,41 @@
+#include "client.hpp"
+
+#include <boost/asio/placeholders.hpp>
+#include <boost/bind.hpp>
+
+#include <iostream>
+
+using namespace boost::asio::ip;
+using namespace boost::system;
+
+Client::Client(tcp::socket &&socket)
+: socket(std::move(socket))
+, buffer()
+{}
+
+void Client::handle_receive(MessageHandler message_handler, const error_code &ec, size_t read_bytes)
+{
+ buffer.commit(read_bytes);
+ std::istream is(&buffer);
+
+ if(!ec) {
+ std::vector<uint8_t> data(std::istream_iterator<uint8_t>(is), {});
+ message_handler(data);
+ receive(message_handler);
+ } else {
+ done();
+ }
+}
+
+void Client::receive(MessageHandler message_handler) {
+ using namespace boost::asio::placeholders;
+
+ socket.async_receive(
+ buffer.prepare(512),
+ boost::bind(&Client::handle_receive, this, message_handler, error(), bytes_transferred())
+ );
+}
+
+void Client::on_done(Client::OnDoneFT f) {
+ done = f;
+}
diff --git a/libcmix-network/client.hpp b/libcmix-network/client.hpp
new file mode 100644
index 0000000..2b6a6df
--- /dev/null
+++ b/libcmix-network/client.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/streambuf.hpp>
+
+#include <array>
+
+class Client {
+public:
+ typedef std::function<void(void)> OnDoneFT;
+ typedef std::function<void(std::vector<uint8_t>)> MessageHandler;
+
+private:
+ boost::asio::ip::tcp::socket socket;
+ boost::asio::streambuf buffer;
+ OnDoneFT done;
+
+public:
+ Client(boost::asio::ip::tcp::socket&& socket);
+
+ void handle_receive(MessageHandler message_handler, boost::system::error_code const& ec, size_t read_bytes);
+ void receive(MessageHandler message_handler);
+ void on_done(OnDoneFT f);
+};
diff --git a/libcmix-network/nodeclient.cpp b/libcmix-network/nodeclient.cpp
new file mode 100644
index 0000000..9b026ba
--- /dev/null
+++ b/libcmix-network/nodeclient.cpp
@@ -0,0 +1,22 @@
+#include "nodeclient.hpp"
+
+#include <iostream>
+
+NodeClient::NodeClient(boost::asio::ip::tcp::socket &&socket)
+: client(std::move(socket))
+{}
+
+void NodeClient::handle_message(std::vector<uint8_t> message)
+{
+ std::cout << std::string(message.begin(), message.end()) << std::endl;
+}
+
+void NodeClient::receive()
+{
+ client.receive(std::bind(&NodeClient::handle_message, this, std::placeholders::_1));
+}
+
+void NodeClient::on_done(Client::OnDoneFT done) {
+ client.on_done(done);
+}
+
diff --git a/libcmix-network/nodeclient.hpp b/libcmix-network/nodeclient.hpp
new file mode 100644
index 0000000..9a3799a
--- /dev/null
+++ b/libcmix-network/nodeclient.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "client.hpp"
+
+#include <boost/asio/ip/tcp.hpp>
+
+#include <vector>
+
+class NodeClient
+{
+ Client client;
+ void handle_message(std::vector<uint8_t> message);
+
+public:
+ NodeClient(boost::asio::ip::tcp::socket&& socket);
+ virtual ~NodeClient() = default;
+
+ void receive();
+ void on_done(Client::OnDoneFT done);
+};
diff --git a/libcmix-network/server.cpp b/libcmix-network/server.cpp
new file mode 100644
index 0000000..cb2bd34
--- /dev/null
+++ b/libcmix-network/server.cpp
@@ -0,0 +1,31 @@
+#include "server.hpp"
+
+using namespace boost::asio::ip;
+using namespace boost::asio;
+
+Server::Server(io_service& io_service, const ListenSettings& listen_settings, AcceptHandler accept_handler)
+: listen_settings(listen_settings)
+, v4_acceptor(io_service, address_v4::from_string(listen_settings.ipv4_inaddr), listen_settings.port)
+, v6_acceptor(io_service, address_v6::from_string(listen_settings.ipv6_inaddr), listen_settings.port)
+{
+ /*
+ * We can't bind both a v4 and v6 socket to both inaddr4_any and inaddr6_any.
+ * So in case both ipv4 and ipv6 are enabled and both want to bind to their
+ * respective inaddr_any we need to bind to v6 any and disable ipv6 only on
+ * the acceptor socket, else we can just bind to the interfaces normally.
+ */
+ bool bind_v4_any = v4_acceptor.get_address().to_v4() == address_v4::any() && listen_settings.enable_ipv4;
+ bool bind_v6_any = v6_acceptor.get_address().to_v6() == address_v6::any() && listen_settings.enable_ipv6;
+ if(bind_v4_any && bind_v6_any) {
+ v6_acceptor.bind_v6_and_v4_any(accept_handler);
+ } else if(bind_v4_any || bind_v6_any) {
+ throw std::runtime_error("Cannot bind an INADDR_ANY and a non INADDR_ANY address on ipv4 and ipv6");
+ } else {
+ if(listen_settings.enable_ipv4) {
+ v4_acceptor.setup_listen_socket(accept_handler);
+ }
+ if(listen_settings.enable_ipv6) {
+ v6_acceptor.setup_listen_socket(accept_handler);
+ }
+ }
+} \ No newline at end of file
diff --git a/libcmix-network/server.hpp b/libcmix-network/server.hpp
new file mode 100644
index 0000000..942cb14
--- /dev/null
+++ b/libcmix-network/server.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <boost/asio/io_service.hpp>
+
+#include "acceptor.hpp"
+
+struct ListenSettings {
+ bool enable_ipv4;
+ std::string ipv4_inaddr;
+ bool enable_ipv6;
+ std::string ipv6_inaddr;
+ uint16_t port;
+};
+
+class Server
+{
+public:
+ typedef std::function<void(boost::asio::ip::tcp::socket&& socket)> AcceptHandler;
+
+private:
+ ListenSettings const& listen_settings;
+
+ Acceptor v4_acceptor;
+ Acceptor v6_acceptor;
+
+public:
+ Server(boost::asio::io_service& io_service, ListenSettings const& listen_settings, AcceptHandler accept_handler);
+
+};