2015-01-11 11:21:18 +01:00
// EchoServer.cpp
// Implements an Echo server using the LibEvent-based cNetwork API, as a test of that API
2015-01-12 09:38:00 +01:00
#include "Globals.h"
#include <iostream>
#include <string>
#include "OSSupport/Network.h"
2015-01-26 14:46:20 +01:00
#include "OSSupport/NetworkSingleton.h"
2015-01-12 09:38:00 +01:00
/** cTCPLink callbacks that echo everything they receive back to the remote peer. */
class cEchoLinkCallbacks :
public cTCPLink :: cCallbacks
{
2015-01-22 13:00:32 +01:00
// cTCPLink::cCallbacks overrides:
virtual void OnLinkCreated ( cTCPLinkPtr a_Link ) override
2015-01-12 09:38:00 +01:00
{
2015-01-22 13:00:32 +01:00
ASSERT ( m_Link == nullptr );
m_Link = a_Link ;
}
virtual void OnReceivedData ( const char * a_Data , size_t a_Size ) override
{
ASSERT ( m_Link != nullptr );
2015-01-12 09:38:00 +01:00
// Echo the incoming data back to outgoing data:
2016-11-13 17:26:58 +01:00
LOGD ( "%p (%s:%d): Data received (%u bytes), echoing back." , static_cast < void *> ( m_Link . get ()), m_Link -> GetRemoteIP (). c_str (), m_Link -> GetRemotePort (), static_cast < unsigned > ( a_Size ));
2015-01-22 13:00:32 +01:00
m_Link -> Send ( a_Data , a_Size );
2015-01-12 09:38:00 +01:00
LOGD ( "Echo queued" );
2015-01-12 14:58:52 +01:00
// Search for a Ctrl+Z, if found, drop the connection:
for ( size_t i = 0 ; i < a_Size ; i ++ )
{
if ( a_Data [ i ] == '\x1a' )
{
2015-01-22 13:00:32 +01:00
m_Link -> Close ();
m_Link . reset ();
2015-01-12 14:58:52 +01:00
return ;
}
}
2015-01-12 09:38:00 +01:00
}
2015-01-22 13:00:32 +01:00
virtual void OnRemoteClosed ( void ) override
2015-01-12 09:38:00 +01:00
{
2015-01-22 13:00:32 +01:00
ASSERT ( m_Link != nullptr );
2016-11-13 17:26:58 +01:00
LOGD ( "%p (%s:%d): Remote has closed the connection." , static_cast < void *> ( m_Link . get ()), m_Link -> GetRemoteIP (). c_str (), m_Link -> GetRemotePort ());
2015-01-22 13:00:32 +01:00
m_Link . reset ();
2015-01-12 09:38:00 +01:00
}
2015-01-22 13:00:32 +01:00
virtual void OnError ( int a_ErrorCode , const AString & a_ErrorMsg ) override
2015-01-12 09:38:00 +01:00
{
2015-01-22 13:00:32 +01:00
ASSERT ( m_Link != nullptr );
2016-11-13 17:26:58 +01:00
LOGD ( "%p (%s:%d): Error %d in the cEchoLinkCallbacks: %s" , static_cast < void *> ( m_Link . get ()), m_Link -> GetRemoteIP (). c_str (), m_Link -> GetRemotePort (), a_ErrorCode , a_ErrorMsg . c_str ());
2015-01-22 13:00:32 +01:00
m_Link . reset ();
2015-01-12 09:38:00 +01:00
}
2015-01-22 13:00:32 +01:00
/** The link attached to this callbacks instance. */
cTCPLinkPtr m_Link ;
2015-01-12 09:38:00 +01:00
};
2015-01-11 11:21:18 +01:00
2015-01-21 21:12:11 +01:00
class cEchoServerCallbacks :
public cNetwork :: cListenCallbacks
{
2016-03-13 19:39:51 +01:00
virtual cTCPLink :: cCallbacksPtr OnIncomingConnection ( const AString & a_RemoteIPAddress , UInt16 a_RemotePort ) override
2015-01-21 21:12:11 +01:00
{
LOGD ( "New incoming connection(%s:%d)." , a_RemoteIPAddress . c_str (), a_RemotePort );
return std :: make_shared < cEchoLinkCallbacks > ();
}
virtual void OnAccepted ( cTCPLink & a_Link ) override
{
LOGD ( "New client accepted (%s:%d), sending welcome message." , a_Link . GetRemoteIP (). c_str (), a_Link . GetRemotePort ());
// Send a welcome message to each connecting client:
a_Link . Send ( "Welcome to the simple echo server. \r\n " );
LOGD ( "Welcome message queued." );
}
virtual void OnError ( int a_ErrorCode , const AString & a_ErrorMsg ) override
{
LOGWARNING ( "An error occured while listening for connections: %d (%s)." , a_ErrorCode , a_ErrorMsg . c_str ());
}
};
2015-02-20 16:08:21 +01:00
static void DoTest ( void )
2015-01-11 11:21:18 +01:00
{
2015-01-12 09:38:00 +01:00
LOGD ( "EchoServer: starting up" );
2015-01-21 21:12:11 +01:00
cServerHandlePtr Server = cNetwork :: Listen ( 9876 , std :: make_shared < cEchoServerCallbacks > ());
2015-01-12 09:38:00 +01:00
if ( ! Server -> IsListening ())
{
LOGWARNING ( "Cannot listen on port 9876" );
abort ();
}
ASSERT ( Server -> IsListening ());
// Wait for the user to terminate the server:
2015-01-12 14:58:52 +01:00
printf ( "Press enter to close the server. \n " );
2015-01-12 09:38:00 +01:00
AString line ;
std :: getline ( std :: cin , line );
// Close the server and all its active connections:
LOG ( "Server terminating." );
Server -> Close ();
ASSERT ( ! Server -> IsListening ());
2015-01-30 21:24:02 +01:00
Server . reset ();
2015-01-12 14:58:52 +01:00
LOGD ( "Server has been closed." );
2015-01-26 14:46:20 +01:00
}
int main ()
{
DoTest ();
2015-01-12 14:58:52 +01:00
printf ( "Press enter to exit test. \n " );
2015-01-26 14:46:20 +01:00
AString line ;
2015-01-12 14:58:52 +01:00
std :: getline ( std :: cin , line );
2015-01-26 14:46:20 +01:00
cNetworkSingleton :: Get (). Terminate ();
2015-01-12 09:38:00 +01:00
LOG ( "Network test finished." );
2015-01-11 11:21:18 +01:00
return 0 ;
}