2023-11-11 16:43:58 +02:00
/*
* NetworkConnection.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "NetworkConnection.h"
VCMI_LIB_NAMESPACE_BEGIN
2024-05-11 14:32:43 +00:00
NetworkConnection :: NetworkConnection ( INetworkConnectionListener & listener , const std :: shared_ptr < NetworkSocket > & socket , const std :: shared_ptr < NetworkContext > & context )
2023-11-11 16:43:58 +02:00
: socket ( socket )
2024-05-11 15:12:49 +00:00
, timer ( std :: make_shared < NetworkTimer > ( * context ))
2023-11-12 13:27:22 +02:00
, listener ( listener )
2023-11-11 16:43:58 +02:00
{
2023-12-25 21:23:27 +02:00
socket -> set_option ( boost :: asio :: ip :: tcp :: no_delay ( true ));
2024-03-16 12:29:36 +03:00
// iOS throws exception on attempt to set buffer size
constexpr auto bufferSize = 4 * 1024 * 1024 ;
try
{
socket -> set_option ( boost :: asio :: socket_base :: send_buffer_size { bufferSize });
}
catch ( const boost :: system :: system_error & e )
{
logNetwork -> error ( "error setting 'send buffer size' socket option: %s" , e . what ());
}
try
{
socket -> set_option ( boost :: asio :: socket_base :: receive_buffer_size { bufferSize });
}
catch ( const boost :: system :: system_error & e )
{
logNetwork -> error ( "error setting 'receive buffer size' socket option: %s" , e . what ());
}
2023-11-11 16:43:58 +02:00
}
void NetworkConnection :: start ()
{
2024-05-11 14:32:43 +00:00
heartbeat ();
2024-05-11 15:12:49 +00:00
startReceiving ();
}
2024-05-11 14:32:43 +00:00
2024-05-11 15:12:49 +00:00
void NetworkConnection :: startReceiving ()
{
2023-11-11 16:43:58 +02:00
boost :: asio :: async_read ( * socket ,
readBuffer ,
boost :: asio :: transfer_exactly ( messageHeaderSize ),
2024-02-02 15:32:06 +02:00
[ self = shared_from_this ()]( const auto & ec , const auto & endpoint ) { self -> onHeaderReceived ( ec ); });
2023-11-11 16:43:58 +02:00
}
2024-05-11 14:32:43 +00:00
void NetworkConnection :: heartbeat ()
{
constexpr auto heartbeatInterval = std :: chrono :: seconds ( 10 );
2024-05-11 15:12:49 +00:00
timer -> expires_after ( heartbeatInterval );
2024-05-12 19:13:17 +00:00
timer -> async_wait ( [ self = weak_from_this ()]( const auto & ec )
2024-05-11 14:32:43 +00:00
{
if ( ec )
return ;
2024-05-12 19:13:17 +00:00
auto locked = self . lock ();
if ( ! locked )
2024-05-11 14:32:43 +00:00
return ;
2024-05-12 19:13:17 +00:00
locked -> sendPacket ({});
locked -> heartbeat ();
2024-05-11 14:32:43 +00:00
});
}
2024-02-03 22:59:56 +02:00
void NetworkConnection :: onHeaderReceived ( const boost :: system :: error_code & ecHeader )
2023-11-11 16:43:58 +02:00
{
2024-02-03 22:59:56 +02:00
if ( ecHeader )
2023-11-12 13:27:22 +02:00
{
2024-05-12 19:13:17 +00:00
onError ( ecHeader . message ());
2023-11-12 13:27:22 +02:00
return ;
}
2023-11-11 16:43:58 +02:00
if ( readBuffer . size () < messageHeaderSize )
throw std :: runtime_error ( "Failed to read header!" );
uint32_t messageSize ;
2024-02-02 01:27:19 +02:00
readBuffer . sgetn ( reinterpret_cast < char *> ( & messageSize ), sizeof ( messageSize ));
2023-11-11 16:43:58 +02:00
if ( messageSize > messageMaxSize )
2024-02-03 22:59:56 +02:00
{
2024-05-12 19:13:17 +00:00
onError ( "Invalid packet size!" );
2024-02-03 22:59:56 +02:00
return ;
}
2023-11-11 16:43:58 +02:00
2024-02-19 13:46:07 +02:00
if ( messageSize == 0 )
{
2024-05-11 14:32:43 +00:00
//heartbeat package with no payload - wait for next packet
2024-05-11 15:12:49 +00:00
startReceiving ();
2024-02-19 13:46:07 +02:00
return ;
}
2024-02-03 22:59:56 +02:00
boost :: asio :: async_read ( * socket ,
readBuffer ,
boost :: asio :: transfer_exactly ( messageSize ),
[ self = shared_from_this (), messageSize ]( const auto & ecPayload , const auto & endpoint ) { self -> onPacketReceived ( ecPayload , messageSize ); });
2023-11-11 16:43:58 +02:00
}
void NetworkConnection :: onPacketReceived ( const boost :: system :: error_code & ec , uint32_t expectedPacketSize )
{
if ( ec )
{
2024-05-12 19:13:17 +00:00
onError ( ec . message ());
2023-11-12 13:27:22 +02:00
return ;
2023-11-11 16:43:58 +02:00
}
if ( readBuffer . size () < expectedPacketSize )
{
2024-05-19 12:54:51 +00:00
// FIXME: figure out what causes this. This should not be possible without error set
std :: string errorMessage = "Failed to read packet! " + std :: to_string ( readBuffer . size ()) + " bytes read, but " + std :: to_string ( expectedPacketSize ) + " bytes expected!" ;
onError ( errorMessage );
2023-11-11 16:43:58 +02:00
}
2024-02-02 01:27:19 +02:00
std :: vector < std :: byte > message ( expectedPacketSize );
readBuffer . sgetn ( reinterpret_cast < char *> ( message . data ()), expectedPacketSize );
2023-11-12 13:27:22 +02:00
listener . onPacketReceived ( shared_from_this (), message );
2024-05-11 15:12:49 +00:00
startReceiving ();
2023-11-11 16:43:58 +02:00
}
2024-05-14 19:40:20 +00:00
void NetworkConnection :: setAsyncWritesEnabled ( bool on )
{
asyncWritesEnabled = on ;
}
2024-02-02 01:27:19 +02:00
void NetworkConnection :: sendPacket ( const std :: vector < std :: byte > & message )
2023-11-11 16:43:58 +02:00
{
2024-08-12 18:26:30 +00:00
std :: lock_guard lock ( writeMutex );
2024-05-11 08:46:00 +00:00
std :: vector < std :: byte > headerVector ( sizeof ( uint32_t ));
uint32_t messageSize = message . size ();
std :: memcpy ( headerVector . data (), & messageSize , sizeof ( uint32_t ));
2024-05-07 08:34:47 +00:00
2024-05-14 19:40:20 +00:00
// At the moment, vcmilobby *requires* async writes in order to handle multiple connections with different speeds and at optimal performance
// However server (and potentially - client) can not handle this mode and may shutdown either socket or entire asio service too early, before all writes are performed
if ( asyncWritesEnabled )
{
2024-01-21 16:48:36 +02:00
2024-05-14 19:40:20 +00:00
bool messageQueueEmpty = dataToSend . empty ();
dataToSend . push_back ( headerVector );
2024-06-01 11:48:30 +00:00
if ( ! message . empty ())
2024-05-14 19:40:20 +00:00
dataToSend . push_back ( message );
if ( messageQueueEmpty )
doSendData ();
//else - data sending loop is still active and still sending previous messages
}
else
{
boost :: system :: error_code ec ;
boost :: asio :: write ( * socket , boost :: asio :: buffer ( headerVector ), ec );
2024-06-01 11:48:30 +00:00
if ( ! message . empty ())
2024-05-14 19:40:20 +00:00
boost :: asio :: write ( * socket , boost :: asio :: buffer ( message ), ec );
}
2024-05-11 08:46:00 +00:00
}
void NetworkConnection :: doSendData ()
{
if ( dataToSend . empty ())
throw std :: runtime_error ( "Attempting to sent data but there is no data to send!" );
boost :: asio :: async_write ( * socket , boost :: asio :: buffer ( dataToSend . front ()), [ self = shared_from_this ()]( const auto & error , const auto & )
{
self -> onDataSent ( error );
});
}
void NetworkConnection :: onDataSent ( const boost :: system :: error_code & ec )
{
2024-08-12 18:26:30 +00:00
std :: lock_guard lock ( writeMutex );
2024-05-11 08:46:00 +00:00
dataToSend . pop_front ();
if ( ec )
{
2024-05-12 19:13:17 +00:00
onError ( ec . message ());
2024-05-11 08:46:00 +00:00
return ;
}
if ( ! dataToSend . empty ())
doSendData ();
2024-02-02 15:32:06 +02:00
}
2024-05-12 19:13:17 +00:00
void NetworkConnection :: onError ( const std :: string & message )
{
listener . onDisconnected ( shared_from_this (), message );
close ();
}
2024-02-02 15:32:06 +02:00
void NetworkConnection :: close ()
{
boost :: system :: error_code ec ;
socket -> close ( ec );
2024-05-11 15:12:49 +00:00
timer -> cancel ( ec );
2024-02-02 15:32:06 +02:00
2024-02-04 19:56:04 +02:00
//NOTE: ignoring error code, intended
2023-11-11 16:43:58 +02:00
}
VCMI_LIB_NAMESPACE_END