#pragma once #include #include #include #include #include #include "chat_messages.hpp" #include "check_policy.hpp" namespace chat { using namespace chat; using namespace boost; using boost::asio::ip::tcp; template class client_network_manager { /* compile-time checks for whether policies are valid or not */ static_assert(is_valid_receive_policy::value, "Receive policy does not supply neccessary methods!"); static_assert(is_valid_send_policy::value, "Send policy does not supply neccessary methods!"); /* members */ private: asio::io_service& _ios; tcp::socket _socket; asio::streambuf _isb;//<-------. asio::streambuf _osb;//<---. | std::istream _is;//--------|---' std::ostream _os;//--------' std::string _login; /* interface */ public: client_network_manager(asio::io_service& ioservice, tcp::resolver::iterator epit, std::string login) : _ios(ioservice), _socket(_ios), _isb(), _osb(), _is(&_isb), _os(&_osb), _login(login) { connect(epit); } void send(chat_message message) { if (!send_policy::check_msg_length(message)) return; _ios.post([this, message] { asio::async_write(_socket, asio::buffer(message.get()), [](boost::system::error_code, size_t) {}); }); } void close_connection() { _ios.post([this] { _os << chat_message(message::BYE); asio::write(_socket, _osb); // TODO: decide what to do with last server message _socket.close(); _ios.stop(); }); } /* internal methods */ private: static void throw_if_error(boost::system::error_code& ec) { if (ec) throw std::runtime_error("Networking error: " + ec.message()); } void connect(tcp::resolver::iterator epit) { asio::async_connect ( _socket, epit, std::bind(&client_network_manager::handshake, this, std::placeholders::_1, std::placeholders::_2) ); } void handshake(boost::system::error_code ec, tcp::resolver::iterator) { throw_if_error(ec); _os << chat_message(message::HELLO); _os << chat_message(message::NEPTUN, _login); std::reverse(_login.begin(), _login.end()); _os << chat_message(message::PASSW, _login); asio::write(_socket, _osb); // handshake is handled synchronously std::string data; for (int i = 0; i < 4; ++i) // hello + 3 responses for messages above data += receive_message_sync() += '\n'; receive_policy::handshake_do_what(chat_message(message::SERVER_DIRECTION, data)); receive(); // then flow goes to receive-loop } std::string receive_message_sync() { boost::system::error_code ec; std::string data; asio::read_until(_socket, _isb, byte(message::TERM), ec); std::getline(_is, data, byte(message::TERM)); throw_if_error(ec); return data.substr(1); } void receive() { asio::async_read_until (_socket, _isb, byte(message::TERM), [this](boost::system::error_code ec, size_t) { throw_if_error(ec); std::string data; std::getline(_is, data, byte(message::TERM)); char header = data[0]; auto content = data.substr(1, data.size()-1); switch (header) { case byte(message::PING): send(chat_message(message::PONG)); break; case byte(message::SERVER_DIRECTION): receive_policy::serverdirection_do_what (chat_message(message::SERVER_DIRECTION, content)); break; case byte(message::MESSAGE): receive_policy::message_do_what (chat_message(message::MESSAGE, content)); break; case byte(message::LOGIN): receive_policy::login_do_what (chat_message(message::LOGIN, content)); break; case byte(message::LOGOUT): receive_policy::logout_do_what (chat_message(message::LOGOUT, content)); break; } receive(); }); } }; }