228 lines
6.6 KiB
C++
228 lines
6.6 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
/*
|
|
* Layer 4 and Service example, front-end server.
|
|
*
|
|
* This front-end server expects pings, and forward them to
|
|
* the real ping server. When the ping server sends a pong back,
|
|
* the front-end server forwards it to the client.
|
|
*
|
|
* Even if the connection to the ping server is broken, our
|
|
* front-end server will keep storing the ping messages and
|
|
* will forward them when the connection is restored.
|
|
*
|
|
* To run this program, ensure there is a file "frontend_service.cfg"
|
|
* containing the location of the naming service (NSHost, NSPort)
|
|
* in the working directory. The naming service must be running.
|
|
*
|
|
* DEPRECATED: You should use layer5 (take a look in the layer5 sample directory)
|
|
*
|
|
*/
|
|
|
|
|
|
// We're using the NeL Service framework and layer 4.
|
|
#include "nel/net/service.h"
|
|
#include "nel/net/net_manager.h"
|
|
using namespace NLNET;
|
|
|
|
#include <deque>
|
|
using namespace std;
|
|
|
|
|
|
// Storage (a queue because the connection to the ping service is reliable, the order is preserved)
|
|
deque< pair<TSockId,uint32> > ClientIds;
|
|
|
|
|
|
/*
|
|
* Callback function called when receiving a "PING" message
|
|
*
|
|
* Arguments:
|
|
* - msgin: the incoming message (coming from a client)
|
|
* - from: the "sockid" of the sender client
|
|
* - frontendserver: the CCallbackNetBase object (which really is a CCallbackServer object, for a server)
|
|
*
|
|
* Input (expected message from a client): PING
|
|
* - uint32: ping counter
|
|
*
|
|
* Output (sent message to the ping server): PONG
|
|
* - uint32: ping counter
|
|
*/
|
|
void cbPing( CMessage& msgin, TSockId from, CCallbackNetBase& frontendserver )
|
|
{
|
|
uint32 counter;
|
|
|
|
// Input
|
|
msgin.serial( counter );
|
|
ClientIds.push_back( make_pair( from, counter ) ); // store client sockid
|
|
|
|
// Output
|
|
CMessage msgout( "PING" );
|
|
msgout.serial( counter );
|
|
CNetManager::send( "PS", msgout ); // does not send if not connected
|
|
|
|
nlinfo( "Received PING number %u from %s", counter, frontendserver.hostAddress(from).asString().c_str() );
|
|
}
|
|
|
|
|
|
/*
|
|
* Callback function called when receiving a "PONG" message
|
|
*
|
|
* Arguments:
|
|
* - msgin: the incoming message (coming from the ping server)
|
|
* - from: the "sockid" of the sender (usually useless for a CCallbackClient)
|
|
* - clientofthepingserver: the CCallbackNetBase object (which really is a CCallbackClient object)
|
|
*
|
|
* Input (expected message from the ping server): PONG
|
|
* - uint32: ping counter
|
|
* - TSockId: "sock id" of the client who sent the ping
|
|
*
|
|
* Output (sent message to a client): PONG
|
|
* - uint32: ping counter
|
|
*/
|
|
void cbPong( CMessage& msgin, TSockId from, CCallbackNetBase& clientofthepingserver )
|
|
{
|
|
uint32 counter;
|
|
TSockId clientfrom;
|
|
|
|
// Input: process the reply of the ping service
|
|
msgin.serial( counter );
|
|
|
|
// Do not send in case of double ping service reply (see onDisconnectPS())
|
|
// (does not work if two clients send the same counter at the same time)
|
|
if ( ( !ClientIds.empty() ) && (ClientIds.front().second == counter) )
|
|
{
|
|
clientfrom = ClientIds.front().first; // retrieve client sockid
|
|
ClientIds.pop_front();
|
|
|
|
// Output: send the reply to the client
|
|
CMessage msgout( "PONG" );
|
|
msgout.serial( counter );
|
|
CNetManager::send( "FS", msgout, clientfrom );
|
|
|
|
nlinfo( "Sent PONG number %u to %s", counter, clientfrom->asString().c_str() );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Disonnection callback for the ping service
|
|
*/
|
|
void onDisconnectPS( const std::string &serviceName, TSockId from, void *arg )
|
|
{
|
|
/* Note: the pings already forwarded should get no reply, but it may occur
|
|
* (e.g. if the server reconnects before the forwarding of a PING and
|
|
* the reconnection callbacks is called after that). Then onReconnectPS()
|
|
* may send PINGs that have already been sent and the front-end may get
|
|
* the same PONG twice. This is partially handled in cbPong.
|
|
*/
|
|
|
|
nlinfo( "Ping Service disconnecting: pongs will be delayed until reconnection" );
|
|
}
|
|
|
|
|
|
/*
|
|
* Connection callback for the ping service
|
|
*/
|
|
void onReconnectPS( const std::string &serviceName, TSockId from, void *arg )
|
|
{
|
|
uint32 i;
|
|
uint32 counter;
|
|
|
|
// Output: forward all stored pings to the reconnected ping service
|
|
for ( i=0; i!=ClientIds.size(); i++ )
|
|
{
|
|
CMessage msgout( "PING" );
|
|
counter = ClientIds[i].second;
|
|
msgout.serial( counter );
|
|
CNetManager::send( "PS", msgout );
|
|
}
|
|
|
|
nlinfo( "Ping Service reconnected: %d pings forwarded", ClientIds.size() );
|
|
}
|
|
|
|
|
|
/*
|
|
* Disonnection callback for a client
|
|
*/
|
|
void onDisconnectClient( const std::string &serviceName, TSockId from, void *arg )
|
|
{
|
|
// Erase all associated elements in the queue
|
|
deque< pair<TSockId,uint32> >::iterator iq = ClientIds.begin();
|
|
while ( iq!=ClientIds.end() )
|
|
{
|
|
if ( (*iq).first == from )
|
|
{
|
|
iq = ClientIds.erase( iq );
|
|
}
|
|
else
|
|
{
|
|
iq++;
|
|
}
|
|
}
|
|
|
|
nlinfo( "A client has disconnected" );
|
|
}
|
|
|
|
|
|
/*
|
|
* Callback array for messages received from a client
|
|
*/
|
|
TCallbackItem CallbackArray[] =
|
|
{
|
|
{ "PING", cbPing } // when receiving a "PING" message, call cbPing()
|
|
};
|
|
|
|
|
|
/*
|
|
* Callback array for message received from the ping service
|
|
*/
|
|
TCallbackItem PingServiceCallbackArray[] =
|
|
{
|
|
{ "PONG", cbPong } // when receiving a "PONG" message, call cbPong()
|
|
};
|
|
|
|
|
|
/*
|
|
* CFrontEndService, based on IService
|
|
*/
|
|
class CFrontEndService : public IService
|
|
{
|
|
public:
|
|
|
|
/*
|
|
* Initialization
|
|
*/
|
|
void init()
|
|
{
|
|
// Connect to the ping service
|
|
CNetManager::addClient( "PS" );
|
|
CNetManager::addCallbackArray( "PS", PingServiceCallbackArray, sizeof(PingServiceCallbackArray)/sizeof(PingServiceCallbackArray[0]) );
|
|
CNetManager::setConnectionCallback( "PS", onReconnectPS, NULL );
|
|
CNetManager::setDisconnectionCallback( "PS", onDisconnectPS, NULL );
|
|
|
|
CNetManager::setDisconnectionCallback( "FS", onDisconnectClient, NULL );
|
|
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* Declare a service with the class CFrontEndService, the names "FS" (short) and "frontend_service" (long).
|
|
* The port is set to 37000 and the main callback array is CallbackArray.
|
|
*/
|
|
NLNET_OLD_SERVICE_MAIN( CFrontEndService, "FS", "frontend_service", 37000, CallbackArray, "", "" )
|