diff --git a/lib/network/NetworkConnection.cpp b/lib/network/NetworkConnection.cpp index b28daadad..5d41a7d10 100644 --- a/lib/network/NetworkConnection.cpp +++ b/lib/network/NetworkConnection.cpp @@ -12,12 +12,12 @@ VCMI_LIB_NAMESPACE_BEGIN -NetworkConnection::NetworkConnection(INetworkConnectionListener & listener, const std::shared_ptr & socket) +NetworkConnection::NetworkConnection(INetworkConnectionListener & listener, const std::shared_ptr & socket, const std::shared_ptr & context) : socket(socket) + , context(context) , listener(listener) { socket->set_option(boost::asio::ip::tcp::no_delay(true)); - socket->set_option(boost::asio::socket_base::keep_alive(true)); // iOS throws exception on attempt to set buffer size constexpr auto bufferSize = 4 * 1024 * 1024; @@ -43,12 +43,32 @@ NetworkConnection::NetworkConnection(INetworkConnectionListener & listener, cons void NetworkConnection::start() { + heartbeat(); + boost::asio::async_read(*socket, readBuffer, boost::asio::transfer_exactly(messageHeaderSize), [self = shared_from_this()](const auto & ec, const auto & endpoint) { self->onHeaderReceived(ec); }); } +void NetworkConnection::heartbeat() +{ + constexpr auto heartbeatInterval = std::chrono::seconds(10); + + auto timer = std::make_shared(*context, heartbeatInterval); + timer->async_wait( [self = shared_from_this(), timer](const auto & ec) + { + if (ec) + return; + + if (!self->socket->is_open()) + return; + + self->sendPacket({}); + self->heartbeat(); + }); +} + void NetworkConnection::onHeaderReceived(const boost::system::error_code & ecHeader) { if (ecHeader) @@ -71,7 +91,8 @@ void NetworkConnection::onHeaderReceived(const boost::system::error_code & ecHea if (messageSize == 0) { - listener.onDisconnected(shared_from_this(), "Zero-sized packet!"); + //heartbeat package with no payload - wait for next packet + start(); return; } diff --git a/lib/network/NetworkConnection.h b/lib/network/NetworkConnection.h index beaaba376..f615ae45e 100644 --- a/lib/network/NetworkConnection.h +++ b/lib/network/NetworkConnection.h @@ -19,15 +19,17 @@ class NetworkConnection : public INetworkConnection, public std::enable_shared_f static const int messageMaxSize = 64 * 1024 * 1024; // arbitrary size to prevent potential massive allocation if we receive garbage input std::shared_ptr socket; + std::shared_ptr context; NetworkBuffer readBuffer; INetworkConnectionListener & listener; + void heartbeat(); void onHeaderReceived(const boost::system::error_code & ec); void onPacketReceived(const boost::system::error_code & ec, uint32_t expectedPacketSize); public: - NetworkConnection(INetworkConnectionListener & listener, const std::shared_ptr & socket); + NetworkConnection(INetworkConnectionListener & listener, const std::shared_ptr & socket, const std::shared_ptr & context); void start(); void close() override; diff --git a/lib/network/NetworkHandler.cpp b/lib/network/NetworkHandler.cpp index ddb15e091..3e5891bca 100644 --- a/lib/network/NetworkHandler.cpp +++ b/lib/network/NetworkHandler.cpp @@ -34,14 +34,14 @@ void NetworkHandler::connectToRemote(INetworkClientListener & listener, const st auto socket = std::make_shared(*io); boost::asio::ip::tcp::resolver resolver(*io); auto endpoints = resolver.resolve(host, std::to_string(port)); - boost::asio::async_connect(*socket, endpoints, [socket, &listener](const boost::system::error_code& error, const boost::asio::ip::tcp::endpoint& endpoint) + boost::asio::async_connect(*socket, endpoints, [this, socket, &listener](const boost::system::error_code& error, const boost::asio::ip::tcp::endpoint& endpoint) { if (error) { listener.onConnectionFailed(error.message()); return; } - auto connection = std::make_shared(listener, socket); + auto connection = std::make_shared(listener, socket, io); connection->start(); listener.onConnectionEstablished(connection); diff --git a/lib/network/NetworkServer.cpp b/lib/network/NetworkServer.cpp index b408ed525..43f573f37 100644 --- a/lib/network/NetworkServer.cpp +++ b/lib/network/NetworkServer.cpp @@ -39,7 +39,7 @@ void NetworkServer::connectionAccepted(std::shared_ptr upcomingCo } logNetwork->info("We got a new connection! :)"); - auto connection = std::make_shared(*this, upcomingConnection); + auto connection = std::make_shared(*this, upcomingConnection, io); connections.insert(connection); connection->start(); listener.onNewConnection(connection);