diff options
| author | Dennis Brentjes <d.brentjes@gmail.com> | 2016-08-31 11:57:15 +0200 |
|---|---|---|
| committer | Dennis Brentjes <d.brentjes@gmail.com> | 2016-08-31 11:57:15 +0200 |
| commit | 2f1c3293d2c5776c4ecd9b2f1dce66492b15dbdd (patch) | |
| tree | 1c582e4c5715a93c493582b1e01e51bc960d56c0 /libcmix-network | |
| parent | 1525c5defe3db08c765477003be73c68bb2c3cb7 (diff) | |
| download | cmix-2f1c3293d2c5776c4ecd9b2f1dce66492b15dbdd.tar.gz cmix-2f1c3293d2c5776c4ecd9b2f1dce66492b15dbdd.tar.bz2 cmix-2f1c3293d2c5776c4ecd9b2f1dce66492b15dbdd.zip | |
Split up the client and server parts in a separate network library
Diffstat (limited to 'libcmix-network')
| -rw-r--r-- | libcmix-network/CMakeLists.txt | 29 | ||||
| -rw-r--r-- | libcmix-network/acceptor.cpp | 59 | ||||
| -rw-r--r-- | libcmix-network/acceptor.hpp | 18 | ||||
| -rw-r--r-- | libcmix-network/client.cpp | 41 | ||||
| -rw-r--r-- | libcmix-network/client.hpp | 24 | ||||
| -rw-r--r-- | libcmix-network/nodeclient.cpp | 22 | ||||
| -rw-r--r-- | libcmix-network/nodeclient.hpp | 20 | ||||
| -rw-r--r-- | libcmix-network/server.cpp | 31 | ||||
| -rw-r--r-- | libcmix-network/server.hpp | 29 |
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); + +}; |
