#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; struct receive_policy_stdout { static void message_do_what(chat_message msg) { std::cout << msg.get_content() << '\n'; } static void handshake_do_what(chat_message msg) { std::cout << "Handshake üzenetek:\n" << msg.get_content() << '\n'; } static void serverdirection_do_what(chat_message msg) { std::cout << "Szerver üzenet: " << msg.get_content() << '\n'; } static void login_do_what(chat_message msg) { std::cout << msg.get_content() << " belépett.\n"; } static void logout_do_what(chat_message msg) { std::cout << msg.get_content() << " kilépett.\n"; } }; /* most ezt így üres template paraméterekkel kell példányosítani, * már amennyiben nem akarunk valami custom receive policyt megadni, * pl.: client_network_manager<> cnm; * meg lehet oldani úgy, hogy ne kelljen? usingot/typedefet nem enged saját magára */ template class client_network_manager { /* compile-time check for whether receive_policy is valid or not */ static_assert(is_valid_policy::value, "Receive 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) { _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(); }); } }; }