// 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 3 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. * * 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. */ // We're building a server, using the NeL Service framework. // We need the naming service to know where the ping service is, so we're using CNamingClient. // We're using CCallbackClient when we talk to the naming service. // We're using CCallbackServer when we talk to our clients. #include "nel/net/service.h" #include "nel/net/naming_client.h" #include "nel/net/callback_client.h" #include "nel/net/callback_server.h" using namespace NLNET; #include <deque> using namespace std; // The front-end server is also a client of the ping service CCallbackClient *ToPingService; // Temp storage (a queue because the connection to the ping service is reliable, the order is preserved) deque<TSockId> 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( from ); // store client sockid // Output CMessage msgout( "PING" ); msgout.serial( counter ); vector<uint8> vect( 400000 ); msgout.serialCont( vect ); ToPingService->send( msgout ); nlinfo( "Received PING number %u from %s", counter, frontendserver.hostAddress(from).asString().c_str() ); } /* * Disconnection callback, called when a client disconnects */ void discCallback( TSockId from, void *p ) { // Remove all occurences of from in the queue deque<TSockId>::iterator iq; for ( iq=ClientIds.begin(); iq!=ClientIds.end(); ) { if ( *iq == from ) { iq = ClientIds.erase( iq ); } else { iq++; } } } /* * 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 ); clientfrom = ClientIds.front(); // retrieve client sockid ClientIds.pop_front(); // Output: send the reply to the client CCallbackServer *server = IService::getInstance()->getServer(); CMessage msgout( "PONG" ); msgout.serial( counter ); server->send( msgout, clientfrom ); nlinfo( "Sent PONG number %u to %s", counter, clientfrom->asString().c_str() ); } /* * 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 ToPingService = new CCallbackClient( IService::getRecordingState(), "PS.nmr" ); ToPingService->addCallbackArray( PingServiceCallbackArray, sizeof(PingServiceCallbackArray)/sizeof(PingServiceCallbackArray[0]) ); if ( ! CNamingClient::lookupAndConnect( "PS", *ToPingService ) ) { nlerror( "Ping Service not available" ); } // Disconnection callback for the clients IService::getServer()->setDisconnectionCallback( discCallback, NULL ); } /* * Update */ bool update() { ToPingService->update( 20 ); // 20 ms max return ToPingService->connected(); // true continues, false stops the service } /* * Finalization */ void release() { delete ToPingService; } }; /* * 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, "", "" )