khanat-opennel-code/code/nel/samples/net/net_layer4/frontend_service.cpp
2011-04-27 19:42:44 +03:00

228 lines
6.8 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, "", "" )