#include "client.hpp" #include "connect.hpp" #include "logging.hpp" #include #include #include #include #include using namespace boost::asio::ip; using namespace boost::system; Client::Client(tcp::socket &&socket) : socket(std::move(socket)) , buffer() , done() {} Client::~Client() { close(); } void Client::async_connect(std::string next_host, std::string next_port, std::function on_connect) { ::async_connect(socket, next_host, next_port, on_connect); } std::array Client::prepare_length_prefix(uint32_t length) { length = htonl(length); std::array buf; std::copy(reinterpret_cast(&length), reinterpret_cast(&length) + 4, buf.data()); return buf; } void Client::send(std::string message) { auto length_buffer = prepare_length_prefix(message.size()); boost::array package = { boost::asio::buffer(length_buffer.data(), length_buffer.size()), boost::asio::buffer(message) }; auto handler = [](boost::system::error_code const& ec, std::size_t bytes_transferred) { if(ec) { BOOST_LOG_TRIVIAL(fatal) << ec; throw std::runtime_error("unable to send message"); } }; socket.async_send(package, 0, handler); } std::vector Client::received_bytes_to_vector(size_t read_bytes) { buffer.commit(read_bytes); std::istream is(&buffer); is.unsetf(std::ios::skipws); return std::vector(std::istream_iterator(is), {}); } void Client::handle_receive_message(MessageHandler message_handler, const error_code &ec, size_t read_bytes) { if(!ec) { std::vector data = received_bytes_to_vector(read_bytes); message_handler(data); } else { BOOST_LOG_TRIVIAL(error) << ec; if(done) { done(); } } } void Client::handle_receive_size(Client::MessageHandler message_handler, const error_code& ec, size_t read_bytes) { using namespace boost::asio::placeholders; if(!ec) { std::vector data = received_bytes_to_vector(read_bytes); uint32_t size; std::copy(data.begin(), data.end(), reinterpret_cast(&size)); size = ntohl(size); socket.async_receive( buffer.prepare(size), boost::bind(&Client::handle_receive_message, this, message_handler, error(), bytes_transferred()) ); } else { BOOST_LOG_TRIVIAL(error) << ec; if(done) { done(); } } } void Client::receive(MessageHandler message_handler) { using namespace boost::asio::placeholders; socket.async_receive( buffer.prepare(4), boost::bind(&Client::handle_receive_size, this, message_handler, error(), bytes_transferred()) ); } void Client::close() { socket.close(); } void Client::on_done(Client::OnDoneFT f) { done = f; }