cdev-1.7.2n

This commit is contained in:
2022-12-13 12:44:04 +01:00
commit b3b88fc333
1357 changed files with 338883 additions and 0 deletions
+87
View File
@@ -0,0 +1,87 @@
#include "ClientAcceptor.h"
#include "ClientHandler.h"
// *****************************************************************************
// * ClientAcceptor:
// * Default constructor for the ClientAcceptor class
// *****************************************************************************
ClientAcceptor::ClientAcceptor (cdevSessionManager & s)
: server(s)
{
}
// *****************************************************************************
// * ClientAcceptor::~ClientAcceptor:
// * Destructor for the ClientAcceptor class
// *****************************************************************************
ClientAcceptor::~ClientAcceptor (void)
{
if(reactor) reactor->extractHandler(this);
handleClose();
}
// *****************************************************************************
// * open:
// * Initializes the listening socket
// *****************************************************************************
int ClientAcceptor::open (const cdevInetAddr &addr)
{
int result = -1;
if (this->acceptor.open (addr, TRUE) == -1)
{
outputError(CDEV_SEVERITY_SEVERE, (char *)getName(),
"Failed to open listening port");
}
else if (acceptor.setFlags (O_NONBLOCK) == -1)
{
outputError(CDEV_SEVERITY_ERROR, (char *)getName(),
"Could not enable non-blocking I/O");
}
else result = 0;
return result;
}
// *****************************************************************************
// * getHandle:
// * Returns the device descriptor of the listening socket
// *****************************************************************************
int ClientAcceptor::getHandle (void) const
{
return acceptor.getHandle ();
}
// *****************************************************************************
// * handleInput:
// * Accepts a connection on a listening socket and then creates a
// * ClientHandler class to manage the connection.
// *****************************************************************************
int ClientAcceptor::handleInput (void)
{
cdevInetAddr addr;
ClientHandler *svc_handler = new ClientHandler(server);
if (acceptor.accept (*svc_handler, &addr) != -1)
{
svc_handler->open(this);
}
else
{
outputError(CDEV_SEVERITY_ERROR, (char *)getName(),
"Failed to accept connection");
}
// *********************************************************************
// * Always return 0... Otherwise, the accepting socket will
// * be destroyed and a crippled server is all that will remain.
// *********************************************************************
return 0;
}
// *****************************************************************************
// * handleClose:
// * Closes the listening socket
// *****************************************************************************
int ClientAcceptor::handleClose (void)
{
acceptor.close ();
return 0;
}
+34
View File
@@ -0,0 +1,34 @@
#if !defined (_CLIENT_ACCEPTOR_H)
#define _CLIENT_ACCEPTOR_H
#include "cdevSessionManager.h"
#include "cdevSocketAcceptor.h"
// *****************************************************************************
// * class ClientAcceptor:
// * This class provdies the cdevEventHandler that accepts connections through
// * the reactor on the listening port. Once accepted, these new connections
// * are serviced by the ClientHandler.
// *****************************************************************************
class GENERIC_SERVER_API ClientAcceptor : public cdevEventHandler, public ErrorReporter
{
friend class ClientHandler;
public:
ClientAcceptor (cdevSessionManager &s);
~ClientAcceptor (void);
const char *getName (void) { return "ClientAcceptor"; }
int getLocalAddress (cdevAddr &addr) { return acceptor.getLocalAddress(addr); }
int open (const cdevInetAddr &addr);
virtual int getHandle (void) const;
virtual int handleInput (void);
virtual int handleClose (void);
private:
cdevSocketAcceptor acceptor;
cdevSessionManager & server;
};
#endif
+268
View File
@@ -0,0 +1,268 @@
#include "ClientHandler.h"
#include "ClientAcceptor.h"
#include <clipMagicNumber.h>
// *****************************************************************************
// * ClientHandler:
// * Default constructor for the ClientHandler class
// *****************************************************************************
ClientHandler::ClientHandler (cdevSessionManager & s)
: server(s), queue(NULL), clientQuitFlag(0),
SocketReader(CLIP_MAGIC_NUMBER), SocketWriter(CLIP_MAGIC_NUMBER),
packetsSent(0), packetsRecv(0)
{
hostName[0] = 0;
}
// *****************************************************************************
// * ClientHandler::getHostName:
// * This function returns the name of the remote host.
// *****************************************************************************
char * ClientHandler::getHostName( void )
{
if(*hostName==0)
{
cdevInetAddr addr;
if(stream.getRemoteAddress (addr)==0)
{
const char * ptr=addr.getHostName();
if(ptr) strncpy (hostName, ptr, MAXHOSTNAMELEN+1);
}
}
return hostName;
}
// *****************************************************************************
// * open:
// * Initializes the new socket and adds this ClientHandler class to
// * the reactor
// *****************************************************************************
int ClientHandler::open (class ClientAcceptor * )
{
cdevInetAddr addr;
int result = 0;
if (stream.getRemoteAddress (addr) == -1)
{
outputError(CDEV_SEVERITY_SEVERE, (char *)getName(),
"Couldn't get local address");
result = -1;
}
else
{
if (server.Reactor.registerHandler (this, READ_MASK|WRITE_MASK|EXCEPT_MASK)!=0)
{
outputError(CDEV_SEVERITY_SEVERE, (char *)getName(),
"Cannot register handler with reactor");
result = -1;
}
else {
// *****************************************************
// * Get the socket number and use it to identify the
// * socket within the cdevSessionManager.
// *****************************************************
int handle = getHandle();
if((queue = server.findSocket(handle))!=NULL)
{
server.removeSocket(handle);
}
queue = server.addSocket(handle);
outputError(CDEV_SEVERITY_INFO, (char *)getName(),
"Establishing connection to %s on socket %i",
getHostName(), handle);
}
}
return result;
}
// *****************************************************************************
// * getHandle:
// * Returns the device descriptor for the underlying socket
// *****************************************************************************
int ClientHandler::getHandle (void) const
{
return stream.getHandle ();
}
// *****************************************************************************
// * ~ClientHandler:
// * Destructor for the ClientHandler object
// *****************************************************************************
ClientHandler::~ClientHandler (void)
{
if(stream.getHandle()>0 && clientQuitFlag==0) writeGoodbye();
if(reactor) reactor->extractHandler(this);
handleClose();
}
// *****************************************************************************
// * handleClose:
// * Removes the object from the reactor class and then deletes it.
// *****************************************************************************
int ClientHandler::handleClose (void)
{
outputError(CDEV_SEVERITY_INFO, (char *)getName(),
"Terminating connection to %s on socket %i",
getHostName(), queue?queue->getSocketID():0);
stream.close();
if(queue)
{
server.removeSocket(queue->getSocketID());
queue = NULL;
}
return 0;
}
// *****************************************************************************
// # handleInput :
// # This function is called when data is ready to be read from a connected
// # socket. This function will read the data from the socket, and will then
// # submit the buffer to the processIncomingPacket function for processing.
// *****************************************************************************
int ClientHandler::handleInput (void)
{
int retval = 0;
char * buf = NULL;
size_t len = 0;
int result = 1;
// *****************************************************************************
// * Record oriented semantics dictate that the length of the transmission
// * always preceeds the actual data. Therefore, read the length of the
// * transmission into the len variable.
// *****************************************************************************
while(result>0)
{
if(buf==NULL) result = read(&buf, (int *)&len);
else result = readNextPacket(&buf, (int *)&len);
switch(result)
{
// *************************************************************
// * A return value of SHUTDOWN_CODE indicates that a negative
// * one was provided as the packet length - indicating a
// * shutdown...
// *************************************************************
case SHUTDOWN_CODE:
outputError(CDEV_SEVERITY_INFO, (char *)getName(),
"Connection to %s terminated by client", getHostName());
retval = -1;
break;
// *************************************************************
// * A return value of -1 indicates an error occured while
// * reading from the socket. A value of -1 is returned to
// * shutdown the socket and remove it from the reactor.
// *************************************************************
case -1:
outputError(CDEV_SEVERITY_WARN, (char *)getName(),
"Error reading from connection to client %s", getHostName());
retval = -1;
break;
// *************************************************************
// * A return value of 0 means that no data was ready to be
// * retrieved from the socket.
// *************************************************************
case 0:
break;
// *************************************************************
// * Any other value returned from the socket represents the
// * number of bytes actually read into the local buffer object.
// *************************************************************
default:
server.enqueue(getHandle(), buf, len);
packetsSent++;
break;
}
}
// *****************************************************************************
// * Return the value specified by retval.
// *****************************************************************************
return retval;
}
// *****************************************************************************
// # handleOutput :
// # This function is called when data is ready to be transmitted to a
// # connected socket. This function will read the data from the queue,
// # translate it to XDR, and then transmit it to the client.
// *****************************************************************************
int ClientHandler::handleOutput (void)
{
int retval = 0;
char * buf = NULL;
size_t len = 0;
// *****************************************************************************
// * Attempt to transmit or continue transmitting the data. Note, the peek
// * method is used to attempt to writeEnqueue a data item without actually
// * removing it from the outbound queue. If the item can be writeEnqueued,
// * then the dequeue method is called to remove it from the queue...
// * This method is repeated until the output buffer is fully populated.
// *****************************************************************************
if(!writing() && queue!=NULL && queue->peek(&buf, &len)==0)
{
int full = 0;
while(!full)
{
full = writeEnqueue(buf, len);
if(!full)
{
queue->dequeue(&buf, &len);
delete buf;
buf = NULL;
len = 0;
if(queue->peek(&buf, &len)!=0) full=-1;
packetsRecv++;
}
}
if(writeContinue()<0) retval = -1;
}
else if(writing())
{
if(writeContinue()<0) retval = -1;
}
// *****************************************************************************
// * Display an error message if the transmission failed.
// *****************************************************************************
if(retval!=0)
{
outputError(CDEV_SEVERITY_WARN, (char *)getName(),
"Error transmitting to %s", getHostName());
}
// *****************************************************************************
// * If there are no further messages in the outbound queue and the
// * ACE_Event_Handler has finished servicing the current message, turn off the
// * WRITE_MASK for this ACE_Event_Handler.
// *****************************************************************************
if(retval!=0 || ((queue==NULL || queue->empty()) && !writing()))
{
setMask(READ_MASK|EXCEPT_MASK);
}
// *****************************************************************************
// * Return the value specified by retval.
// *****************************************************************************
return retval;
}
// *****************************************************************************
// * ClientHandler::handleExcept :
// * This function is called when out of band data is ready to be received
// * from the socket.
// *****************************************************************************
int ClientHandler::handleExcept(void)
{
clientQuitFlag=1;
return -1;
}
+43
View File
@@ -0,0 +1,43 @@
#if !defined (_CLIENT_HANDLER_H)
#define _CLIENT_HANDLER_H
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "cdevSessionManager.h"
#include "SocketUtil.h"
#include "cdevSocketStream.h"
class GENERIC_SERVER_API ClientHandler : public cdevEventHandler, public SocketReader, public SocketWriter
{
public:
ClientHandler (cdevSessionManager & s);
~ClientHandler (void);
char * getHostName (void);
const char * getName (void) { return "ClientHandler"; }
virtual int open (class ClientAcceptor * acc);
virtual int getHandle (void) const;
operator cdevSocketStream & (void) { return stream; }
virtual int handleClose (void);
virtual int handleInput (void);
virtual int handleOutput (void);
virtual int handleExcept (void);
size_t getPacketsSent (void) { return packetsSent; }
size_t getPacketsRecv (void) { return packetsRecv; }
protected:
char hostName[MAXHOSTNAMELEN + 1];
cdevSocketStream stream;
cdevSessionManager & server;
SocketSession * queue;
int clientQuitFlag;
size_t packetsSent;
size_t packetsRecv;
};
#endif /* _CLIENT_HANDLER_H */
+106
View File
@@ -0,0 +1,106 @@
ARCH = OS
SHOBJ = YES
include ../include/makeinclude/Makefile.$(ARCH)
APPNAME = "CDEV Generic Server Engine"
CXXINCLUDES = -I../include/ace $(ACEINCLUDES)
TEMPLINKS = cdevPacket.cc\
cdevMessageBinary.cc\
cdevMessage.cc\
cdevContextMap.cc\
cdevTagMap.cc\
cdevMonitorTable.cc\
SignalManager.cc\
cdevAddr.cc\
cdevEventHandler.cc\
cdevHandleSet.cc\
cdevReactor.cc\
cdevSocket.cc\
cdevSocketAcceptor.cc\
cdevSocketConnector.cc\
cdevSocketDatagram.cc\
cdevSocketStream.cc\
cdevStreamNode.cc\
cdevStreamQueue.cc\
cdevTime.cc\
fifo.cc\
IntHash.cc
SERVER_OBJS = $(OBJDIR)/cdevServer.o\
$(OBJDIR)/cdevServerTools.o\
$(OBJDIR)/cdevSessionManager.o\
$(OBJDIR)/ClientHandler.o\
$(OBJDIR)/ClientAcceptor.o\
$(OBJDIR)/cdevTagMap.o\
$(OBJDIR)/cdevMonitorTable.o
COMMON_OBJS = $(OBJDIR)/cdevPacket.o\
$(OBJDIR)/cdevMessageBinary.o\
$(OBJDIR)/cdevMessage.o\
$(OBJDIR)/cdevContextMap.o\
$(OBJDIR)/SignalManager.o\
$(OBJDIR)/fifo.o\
$(OBJDIR)/IntHash.cc
ACE_OBJS = $(OBJDIR)/cdevAddr.o\
$(OBJDIR)/cdevEventHandler.o\
$(OBJDIR)/cdevHandleSet.o\
$(OBJDIR)/cdevReactor.o\
$(OBJDIR)/cdevSocket.o\
$(OBJDIR)/cdevSocketAcceptor.o\
$(OBJDIR)/cdevSocketConnector.o\
$(OBJDIR)/cdevSocketDatagram.o\
$(OBJDIR)/cdevSocketStream.o\
$(OBJDIR)/cdevStreamNode.o\
$(OBJDIR)/cdevStreamQueue.o\
$(OBJDIR)/cdevTime.o
# ******************************************************************************
# * The BINARIES definition names all of the binary files that should be deleted
# * whenever "make clean" is executed.
# ******************************************************************************
BINARIES = $(BASELIB)/libcdevServer.$(SHARED_EXT) \
$(BASELIB)/libcdevServer.a
ifeq ($(SHOBJ),YES)
TARGETS = $(TEMPLINKS) $(BASELIB)/libcdevServer.$(SHARED_EXT)
else
TARGETS = $(TEMPLINKS) $(BASELIB)/libcdevServer.a
endif
targets : $(TARGETS)
$(TEMPLINKS) :
@cp $^ $@
$(BASELIB)/libcdevServer.a : $(SERVER_OBJS) $(COMMON_OBJS) $(ACE_OBJS)
$(LINK.a) $@ $^
@$(RANLIB) $@ > /dev/null
@cp $@ $(CDEVLIB)
$(BASELIB)/libcdevServer.$(SHARED_EXT) : $(SERVER_OBJS) $(COMMON_OBJS) $(ACE_OBJS)
$(LINK.so) -o $@ $^ $(NETLIBS)
@cp $@ $(CDEVLIB)
cdevPacket.cc : ../cdevPacket/cdevPacket.cc
cdevMessageBinary.cc : ../cdevPacket/cdevMessageBinary.cc
cdevMessage.cc : ../cdevPacket/cdevMessage.cc
cdevContextMap.cc : ../cdevContextMap/cdevContextMap.cc
cdevTagMap.cc : ../cdevTagMap/cdevTagMap.cc
cdevMonitorTable.cc : ../cdevMonitorTable/cdevMonitorTable.cc
SignalManager.cc : ../common/SignalManager.cc
cdevAddr.cc : ../cdevReactor/cdevAddr.cc
cdevEventHandler.cc : ../cdevReactor/cdevEventHandler.cc
cdevHandleSet.cc : ../cdevReactor/cdevHandleSet.cc
cdevReactor.cc : ../cdevReactor/cdevReactor.cc
cdevSocket.cc : ../cdevReactor/cdevSocket.cc
cdevSocketAcceptor.cc : ../cdevReactor/cdevSocketAcceptor.cc
cdevSocketConnector.cc : ../cdevReactor/cdevSocketConnector.cc
cdevSocketDatagram.cc : ../cdevReactor/cdevSocketDatagram.cc
cdevSocketStream.cc : ../cdevReactor/cdevSocketStream.cc
cdevStreamNode.cc : ../cdevReactor/cdevStreamNode.cc
cdevStreamQueue.cc : ../cdevReactor/cdevStreamQueue.cc
cdevTime.cc : ../cdevReactor/cdevTime.cc
fifo.cc : ../common/fifo.cc
IntHash.cc : ../common/IntHash.cc
@@ -0,0 +1,149 @@
.SUFFIXES: .cc .obj
APPNAME = CDEV Generic Server Engine
ARCH = WINNT-4.0
TEMPLINKS = cdevPacket.cc\
cdevMessageBinary.cc\
cdevMessage.cc\
cdevContextMap.cc\
cdevTagMap.cc\
cdevMonitorTable.cc\
SignalManager.cc\
cdevAddr.cc\
cdevEventHandler.cc\
cdevHandleSet.cc\
cdevReactor.cc\
cdevSocket.cc\
cdevSocketAcceptor.cc\
cdevSocketConnector.cc\
cdevSocketDatagram.cc\
cdevSocketStream.cc\
cdevStreamNode.cc\
cdevStreamQueue.cc\
cdevTime.cc\
fifo.cc
BINARIES = $(CDEVLIB)\cdevServer.dll \
$(CDEVLIB)\cdevServer.lib \
$(TEMPLINKS)
include ..\include\makeinclude\Makefile.WINNT-4.0
SERVER_OBJS = $(OBJDIR)\cdevServer.obj\
$(OBJDIR)\cdevServerTools.obj\
$(OBJDIR)\cdevSessionManager.obj\
$(OBJDIR)\ClientHandler.obj\
$(OBJDIR)\ClientAcceptor.obj\
$(OBJDIR)\cdevTagMap.obj\
$(OBJDIR)\cdevMonitorTable.obj
COMMON_OBJS = $(OBJDIR)\cdevPacket.obj\
$(OBJDIR)\cdevMessageBinary.obj\
$(OBJDIR)\cdevMessage.obj\
$(OBJDIR)\cdevContextMap.obj\
$(OBJDIR)\SignalManager.obj\
$(OBJDIR)\fifo.obj
ACE_OBJS = $(OBJDIR)\cdevAddr.obj\
$(OBJDIR)\cdevEventHandler.obj\
$(OBJDIR)\cdevHandleSet.obj\
$(OBJDIR)\cdevReactor.obj\
$(OBJDIR)\cdevSocket.obj\
$(OBJDIR)\cdevSocketAcceptor.obj\
$(OBJDIR)\cdevSocketConnector.obj\
$(OBJDIR)\cdevSocketDatagram.obj\
$(OBJDIR)\cdevSocketStream.obj\
$(OBJDIR)\cdevStreamNode.obj\
$(OBJDIR)\cdevStreamQueue.obj\
$(OBJDIR)\cdevTime.obj
OBJS = $(SERVER_OBJS) $(COMMON_OBJS) $(ACE_OBJS)
CXXEXTRA = /D "_CDEV_REACTOR_EXPORTS_=1" /D "_GENERIC_SERVER_EXPORTS_=1"
!IF "$(SHOBJ)" == "YES"
TARGETS = $(TEMPLINKS) $(CDEVLIB)\cdevServer.dll
!ELSE
TARGETS = $(TEMPLINKS) $(CDEVLIB)\cdevServer.lib
!ENDIF
targets : $(TARGETS)
@erase $(TEMPLINKS)
$(CDEVLIB)\cdevServer.lib : $(OBJS)
@echo ^ ^ ^ =^> Linking $(@F)
-@if exist $@ erase $@
-@if not exist $(@D) mkdir $(@D)
@$(LIB32) $(CDEVLIB)\cdev.lib $(CDEVLIB)\rsvc.lib\
$(LINK_LIB_FLAGS) /out:$@ $(OBJS)
@echo ^ ^ ^ ^ ^ ^ Done...
$(CDEVLIB)\cdevServer.dll : $(OBJS)
@echo ^ ^ ^ =^> Linking $(@F)
-@if exist $@ erase $@
-@if not exist $(@D) mkdir $(@D)
@$(LIB32) $(CDEVLIB)\cdev.lib $(CDEVLIB)\rsvc.lib\
$(LINK_DLL_FLAGS) /out:$@ /implib:$(@D)\$(@B).lib $(OBJS)
@echo ^ ^ ^ ^ ^ ^ Done...
cdevMessage.cc : ..\cdevPacket\cdevMessage.cc
-@$(CREATE_LINK)
cdevMessageBinary.cc : ..\cdevPacket\cdevMessageBinary.cc
-@$(CREATE_LINK)
cdevPacket.cc : ..\cdevPacket\cdevPacket.cc
-@$(CREATE_LINK)
cdevContextMap.cc : ..\cdevContextMap\cdevContextMap.cc
-@$(CREATE_LINK)
SignalManager.cc : ..\common\SignalManager.cc
-@$(CREATE_LINK)
cdevAddr.cc : ..\cdevReactor\cdevAddr.cc
-@$(CREATE_LINK)
cdevEventHandler.cc : ..\cdevReactor\cdevEventHandler.cc
-@$(CREATE_LINK)
cdevHandleSet.cc : ..\cdevReactor\cdevHandleSet.cc
-@$(CREATE_LINK)
cdevReactor.cc : ..\cdevReactor\cdevReactor.cc
-@$(CREATE_LINK)
cdevSocket.cc : ..\cdevReactor\cdevSocket.cc
-@$(CREATE_LINK)
cdevSocketAcceptor.cc : ..\cdevReactor\cdevSocketAcceptor.cc
-@$(CREATE_LINK)
cdevSocketConnector.cc : ..\cdevReactor\cdevSocketConnector.cc
-@$(CREATE_LINK)
cdevSocketDatagram.cc : ..\cdevReactor\cdevSocketDatagram.cc
-@$(CREATE_LINK)
cdevSocketStream.cc : ..\cdevReactor\cdevSocketStream.cc
-@$(CREATE_LINK)
cdevStreamNode.cc : ..\cdevReactor\cdevStreamNode.cc
-@$(CREATE_LINK)
cdevStreamQueue.cc : ..\cdevReactor\cdevStreamQueue.cc
-@$(CREATE_LINK)
cdevTime.cc : ..\cdevReactor\cdevTime.cc
-@$(CREATE_LINK)
fifo.cc : ..\common\fifo.cc
-@$(CREATE_LINK)
cdevTagMap.cc : ..\cdevTagMap\cdevTagMap.cc
-@$(CREATE_LINK)
cdevMonitorTable.cc : ..\cdevMonitorTable\cdevMonitorTable.cc
-@$(CREATE_LINK)
+692
View File
@@ -0,0 +1,692 @@
#include "cdevServer.h"
#include "ClientAcceptor.h"
#include "ClientHandler.h"
sig_atomic_t cdevServer::Finished = 0;
SignalManager cdevServer::SigManager;
cdevGenericServerTagDef cdevServer::tags;
// *****************************************************************************
// * This function is an interrupt handler that will be executed whenever the
// * program receives a SIGINT. When called it will set the finished flag to 1
// * and trigger the termination of the program.
// *****************************************************************************
static void SIGINT_handler (int signo)
{
cdevServer::Finished = 1;
signal(signo, SIGINT_handler);
}
// *****************************************************************************
// * cdevServer::cdevServer :
// * This is the constructor for the class. It will post the listening
// * socket, establish registration with the name server and record all
// * other pertinent information.
// *****************************************************************************
cdevServer::cdevServer (char * DomainName, char * ServerName, unsigned short Port, double Rate)
: cdevSessionManager(),
serverName (NULL),
acceptor (NULL),
timer (NULL),
status (UNINITIALIZED),
serverInfo (NULL)
{
// *********************************************************************
// * Register the import method for the cdevMessage class with the
// * cdevPacket class.
// *********************************************************************
cdevPacket::registerImportMethod(cdevMessage::CDEV_PACKET_VERSION,
cdevMessage::import);
startServer(DomainName, ServerName, Port, Rate, 0);
}
// *****************************************************************************
// * cdevServer::cdevServer :
// * This is the do nothing constructor for the class.
// *****************************************************************************
cdevServer::cdevServer ( void )
: cdevSessionManager (),
serverName (NULL),
acceptor (NULL),
timer (NULL),
status (UNINITIALIZED),
serverInfo (NULL)
{
// *********************************************************************
// * Register the import method for the cdevMessage class with the
// * cdevPacket class.
// *********************************************************************
cdevPacket::registerImportMethod(cdevMessage::CDEV_PACKET_VERSION,
cdevMessage::import);
}
// *****************************************************************************
// * cdevServer::~cdevServer :
// * This is the destructor for the object. It will close all listening
// * sockets and will delete all allocated items.
// *****************************************************************************
cdevServer::~cdevServer ( void )
{
outputError ( CDEV_SEVERITY_INFO, "CDEV Server",
"Server %s is Terminating...",
serverName);
Reactor.extractHandler(this);
if(timer!=NULL) delete timer;
if(serverName!=NULL) delete serverName;
if(acceptor!=NULL) delete acceptor;
}
// *****************************************************************************
// * cdevServer::startServer :
// * This method will initialize a cdevServer within a given name, will
// * register it within the CDEV Name Server and will commence listening
// * for connections on the specified port.
// *****************************************************************************
int cdevServer::startServer (char * DomainName, char * ServerName, unsigned short Port, double Rate, int searchForPort)
{
if(acceptor!=NULL)
{
delete acceptor;
acceptor=NULL;
}
if(timer)
{
delete timer;
timer=NULL;
}
if(serverName)
{
delete serverName;
serverName = NULL;
}
if(serverInfo)
{
delete serverInfo;
serverInfo = NULL;
}
Reactor.extractHandler(this);
setTimeoutRate(Rate);
serverName = strdup(ServerName);
status = UNINITIALIZED;
acceptor = new ClientAcceptor(*this);
// *********************************************************************
// * Attempt to open the acceptor to receive new connections.
// *********************************************************************
if(searchForPort) Port = 0;
cdevInetAddr addr(Port);
int initialized=!(acceptor->open(addr));
// *********************************************************************
// * Call getLocalAddress to retrieve the actual port number that was
// * opened. This value will differ from the specified port if...
// * 1) the user specified 0 for the port number -or-
// * 2) the user set the searchForPort flag to non-zero.
// *********************************************************************
if(acceptor->getLocalAddress(addr)==0) Port = addr.getPortNum();
else Port = (unsigned short)-1;
// *********************************************************************
// * ServerInfo must be created after the port has been searched for
// * and found. Otherwise, the server will report the original port
// * number rather than the actual port number.
// *********************************************************************
serverInfo = new ServerInfo(DomainName, ServerName, Port);
if(initialized)
{
// *************************************************************
// * Register the acceptor with the Reactor.
// *************************************************************
if(Reactor.registerHandler(acceptor, READ_MASK)!=-1)
{
// *****************************************************
// * Register this event handler with the Reactor.
// *****************************************************
if(Reactor.registerHandler(this, READ_MASK)!=-1)
{
// *********************************************
// * Schedule the periodic timer if it is
// * greater than 0.
// *********************************************
if(Rate <= 0.0 ||
Reactor.registerTimer(this)!=-1)
{
// *************************************
// * Install a name server timer to
// * continuously register this server
// * with the Name Server.
// *************************************
timer = new cdevNameServerManager(Reactor, DomainName, ServerName, Port);
outputError (CDEV_SEVERITY_INFO, "CDEV Server",
"Server %s is operational and servicing requests...",
serverName);
}
else
{
outputError ( CDEV_SEVERITY_SEVERE, "CDEV Server",
"Server %s - Unable to register server timer with cdevReactor",
serverName);
status = CANT_REGISTER_TIMER;
}
}
else
{
outputError ( CDEV_SEVERITY_SEVERE, "CDEV Server",
"Server %s - Unable to register server handler with cdevReactor",
serverName);
status = CANT_REGISTER_SERVER;
}
}
else
{
outputError (CDEV_SEVERITY_SEVERE, "CDEV Server",
"Server %s - Unable to register listening service with cdevReactor",
serverName);
status = CANT_REGISTER_LISTENER;
}
}
else
{
outputError ( CDEV_SEVERITY_SEVERE, "CDEV Server",
"Server %s - Unable to open listening socket",
serverName);
status = CANT_OPEN_SOCKET;
}
switch (status)
{
case CANT_REGISTER_TIMER:
Reactor.extractHandler(this);
case CANT_REGISTER_SERVER:
Reactor.extractHandler(acceptor);
case CANT_REGISTER_LISTENER:
case CANT_OPEN_SOCKET:
delete acceptor;
acceptor = NULL;
break;
case SUCCESS:
default:
status = SUCCESS;
break;
}
return status;
}
// ***************************************************************************
// * cdevServer::runServer :
// * This method is called to simultaneously execute all servers that
// * reside in the system.
// ***************************************************************************
void cdevServer::runServer ( void )
{
SigManager.installDefaults();
SigManager.installHandler (SIGINT, SIGINT_handler);
while (!Finished) Reactor.handleEvents ();
}
// *****************************************************************************
// * cdevServer::registerClient :
// * This is the cdevMessage specific method that is used to send a
// * "register" message to the server.
// *****************************************************************************
void cdevServer::registerClient ( short localID )
{
cdevMessage message(localID,0,0,0,0,0,0,0,NULL,"register");
cdevPacketBinary * packet = new cdevPacketBinary;
char * binary;
size_t binaryLen;
message.streamOut (&binary, &binaryLen);
packet->attachData(binary, binaryLen);
inbound.enqueue (packet);
}
// *****************************************************************************
// * cdevServer::unregisterClient :
// * This is the cdevMessage specific method that is used to send an
// * "unregister" message to the server.
// *****************************************************************************
void cdevServer::unregisterClient ( short localID )
{
cdevMessage message(localID,0,0,0,0,0,0,0,NULL,"unregister");
cdevPacketBinary * packet = new cdevPacketBinary;
char * binary;
size_t binaryLen;
message.streamOut (&binary, &binaryLen);
packet->attachData(binary, binaryLen);
inbound.enqueue (packet);
trigger.insertEvent();
}
// *****************************************************************************
// * cdevServer::newClientSession :
// * This method allows the caller to create a new ClientSession object. The
// * ClientSession object functions primarily as a pointer to the queue that
// * holds packets destined for a specific client, however, the developer
// * can create a subclass of the ClientSession that may be used to associate
// * additional, client specific information to the structure.
// *
// * The CLIPClientSession class allows the developer to associate a context
// * with the clientID.
// *****************************************************************************
ClientSession * cdevServer::newClientSession ( int SocketID, int ClientID, int LocalID )
{
return new CLIPClientSession (SocketID, ClientID, LocalID);
}
// *****************************************************************************
// * cdevServer::newSocketSession :
// * This method allows the caller to create a new SocketSession object. The
// * SocketSession object functions primarily as a pointer to the queue that
// * holds packets destined for a specific socket, however, the developer
// * can create a subclass of the SocketSession that may be used to associate
// * additional, socket specific information to the structure.
// *
// * The CLIPSocketSession class allows the developer to associate a
// * context map and a tag table with the socket number.
// *****************************************************************************
SocketSession * cdevServer::newSocketSession ( int SocketID )
{
if(serverInfo) serverInfo->clientCnt++;
return new CLIPSocketSession (SocketID);
}
// *****************************************************************************
// * cdevServer::deleteSocketSession :
// * This method is called to delete an existing socket session.
// *****************************************************************************
void cdevServer::deleteSocketSession ( SocketSession *socket )
{
if(socket)
{
if(serverInfo)
{
if(serverInfo->clientCnt>0) serverInfo->clientCnt--;
else serverInfo->clientCnt=0;
}
sockets.remove(socket->getSocketID());
delete socket;
}
}
// *****************************************************************************
// * cdevServer::dequeue :
// * This method provides a mechanism for the cdevServer object to only
// * dequeue packets that are of the type cdevMessage.
// *****************************************************************************
int cdevServer::dequeue ( cdevMessage * &message )
{
cdevPacket * packet = NULL;
while(packet==NULL && cdevSessionManager::dequeue(packet)==0)
{
if(packet->getVersion()!=cdevMessage::CDEV_PACKET_VERSION)
{
delete packet;
packet = NULL;
}
else if(((cdevMessage *)packet)->getOperationCode()==CDEV_SERVER_OP)
{
processLocal((cdevMessage * &)packet);
delete packet;
packet = NULL;
}
}
message = (cdevMessage *)packet;
return message==NULL?-1:0;
}
// *****************************************************************************
// * cdevServer::processLocal :
// * This method is called to process requests that are being transmitted
// * directly to the cdev Generic Server Engine to set or retrieve statistics
// * about the operation of the server or its clients.
// *****************************************************************************
void cdevServer::processLocal ( cdevMessage * & message )
{
CLIPClientSession * client = NULL;
CLIPSocketSession * socket = NULL;
if((client = (CLIPClientSession *)findLocalClient(message->getClientID()))!=NULL)
{
socket = (CLIPSocketSession *)findSocket(client->getSocketID());
}
if(!strcmp(message->getMessage(), "set ClientInfo"))
{
cdevData * data = message->getData();
if(data && socket) socket->updateClientInfo(*data);
}
else if(!strcmp(message->getMessage(), "get ServerInfo"))
{
if(serverInfo)
{
message->setOperationCode(CDEV_NORMAL_OP);
message->setData(&serverInfo->getServerData(), 1);
}
else {
message->setOperationCode(CDEV_NORMAL_OP);
message->setCompletionCode(-1);
}
enqueue(message);
}
else if(!strcmp(message->getMessage(), "get ClientInfo"))
{
IntHashIterator iter(&sockets);
ClientHandler * handler = NULL;
int socketCnt = 0;
int index = 0;
cdevData result;
iter.first();
while(iter.data()!=NULL)
{
iter++;
socketCnt++;
}
if(socketCnt)
{
char ** username = new char *[socketCnt];
char ** group = new char *[socketCnt];
unsigned * uid = new unsigned [socketCnt];
unsigned * gid = new unsigned [socketCnt];
unsigned * pid = new unsigned [socketCnt];
char ** program = new char *[socketCnt];
char ** commandline = new char *[socketCnt];
unsigned * starttime = new unsigned [socketCnt];
unsigned * connecttime = new unsigned [socketCnt];
char ** host = new char *[socketCnt];
char ** os = new char *[socketCnt];
char ** osrelease = new char *[socketCnt];
char ** osversion = new char *[socketCnt];
char ** machine = new char *[socketCnt];
char ** shell = new char *[socketCnt];
unsigned * socketNum = new unsigned[socketCnt];
unsigned * sendPktCnt = new unsigned[socketCnt];
unsigned * recvPktCnt = new unsigned[socketCnt];
iter.first();
while(index<socketCnt && (socket = (CLIPSocketSession *)iter.data())!=NULL)
{
username[index] = socket->getUsername();
group[index] = socket->getGroup();
uid[index] = socket->getUid();
gid[index] = socket->getGid();
pid[index] = socket->getPid();
program[index] = socket->getProgram();
commandline[index] = socket->getCommandLine();
starttime[index] = (unsigned)socket->getStartTime();
connecttime[index] = (unsigned)socket->getConnectTime();
host[index] = socket->getHost();
os[index] = socket->getOs();
osrelease[index] = socket->getOsRelease();
osversion[index] = socket->getOsVersion();
machine[index] = socket->getMachine();
shell[index] = socket->getShell();
socketNum[index] = socket->getSocketID();
if(Reactor.getHandler(socketNum[index], (cdevEventHandler *&)handler)==0)
{
sendPktCnt[index] = handler->getPacketsSent();
recvPktCnt[index] = handler->getPacketsRecv();
}
else {
sendPktCnt[index] = 0;
recvPktCnt[index] = 0;
}
index++;
iter++;
}
result.insert("username", username, socketCnt); delete username;
result.insert("group", group, socketCnt); delete group;
result.insert("uid", uid, socketCnt); delete uid;
result.insert("gid", gid, socketCnt); delete gid;
result.insert("pid", pid, socketCnt); delete pid;
result.insert("program", program, socketCnt); delete program;
result.insert("commandline", commandline, socketCnt); delete commandline;
result.insert("starttime", starttime, socketCnt); delete starttime;
result.insert("connecttime", connecttime, socketCnt); delete connecttime;
result.insert("host", host, socketCnt); delete host;
result.insert("os", os, socketCnt); delete os;
result.insert("osrelease", osrelease, socketCnt); delete osrelease;
result.insert("osversion", osversion, socketCnt); delete osversion;
result.insert("machine", machine, socketCnt); delete machine;
result.insert("shell", shell, socketCnt); delete shell;
result.insert("socket", socketNum, socketCnt); delete socketNum;
result.insert("sendPktCnt", sendPktCnt, socketCnt); delete sendPktCnt;
result.insert("recvPktCnt", recvPktCnt, socketCnt); delete recvPktCnt;
}
message->setOperationCode(CDEV_NORMAL_OP);
message->setData(&result, 1);
enqueue(message);
}
}
// *****************************************************************************
// * cdevServer::decodePacket :
// * This method is used to perform preprocessing on a newly dequeued binary
// * packet before it is provided to the caller. This method allows the
// * developer to perform special preparations on the packet before providing
// * it to the caller.
// *****************************************************************************
cdevPacket * cdevServer::decodePacket( cdevPacketBinary * input )
{
cdevPacket * packet = NULL;
if(input!=NULL)
{
short version;
input->getVersion(version);
switch (version)
{
// *****************************************************
// * If it is a cdevMessage object, I want to call the
// * cdevMessage specific decodePacket method.
// *****************************************************
case cdevMessage::CDEV_PACKET_VERSION:
packet = decodePacket ((cdevMessage *)cdevPacket::import(*input));
break;
// *****************************************************
// * Use the cdevSessionManager::decodePacket method
// * to handle any other type of packet.
// *****************************************************
default:
packet = cdevSessionManager::decodePacket(input);
break;
}
if(serverInfo) serverInfo->recvPktCnt++;
}
return packet;
}
// *****************************************************************************
// * cdevServer::decodePacket :
// * This decodePacket method is designed specifically to preprocess the
// * data associated with a cdevMessage object.
// *****************************************************************************
cdevPacket * cdevServer::decodePacket (cdevMessage * message )
{
CLIPClientSession * client = NULL;
CLIPSocketSession * socket = NULL;
if(message!=NULL &&
(client = (CLIPClientSession *)findLocalClient(message->getClientID()))!=NULL &&
(socket = (CLIPSocketSession *)findSocket(client->getSocketID()))!=NULL)
{
// *************************************************************
// * If a tagMap has been provided... use it to update
// * the tagMap stored in the SocketSession object.
// *************************************************************
if(message->getTagMap()!=NULL)
{
socket->TagMap().updateTagMap(*message->getTagMap());
message->setTagMap(NULL);
}
// *************************************************************
// * Pass the data and context objects through the tag map to
// * convert their tags from the remote integers to the local
// * integers.
// *************************************************************
if(message->getData()!=NULL)
socket->TagMap().remoteToLocal(*message->getData());
if(message->getContext()!=NULL)
socket->TagMap().remoteToLocal(*message->getContext());
// *************************************************************
// * If a context has been provided by the client side
// * of the connection - perform the following steps...
// *
// * 1) Add the context to the cdevContextMap for this
// * socketID if it does not already exist, and
// * obtain the index that identifies the new
// * context.
// *
// * 2) Delete the context from within the message.
// *
// * 3) Set the context within the message to the
// * current context that is specified in the
// * ClientSession object.
// *
// * 4) Set the saveContext flag in the message to
// * prevent its inadvertant destruction when the
// * message is deleted.
// *************************************************************
if(message->getContext()!=NULL)
{
cdevData * lastContext = client->getContext();
if(lastContext==NULL || *lastContext!=*message->getContext())
{
int contextID = socket->ContextMap().insert(*message->getContext());
client->setContext(socket->ContextMap().find(contextID));
}
}
message->setContext(client->getContext(), 1);
// *************************************************************
// * Toggle the local and foreign data indices.
// *************************************************************
unsigned int index = message->getForeignDataIndex();
message->setForeignDataIndex(message->getLocalDataIndex());
message->setLocalDataIndex (index);
}
return message;
}
// *****************************************************************************
// * cdevSessionManager::encodePacket :
// * This method is used to perform postprocessing on a packet that has been
// * enqueued to be sent to a client. This method allows the developer to
// * perform special preparations on the packet before providing it to the
// * client.
// *****************************************************************************
cdevPacketBinary * cdevServer::encodePacket ( cdevPacket * input )
{
cdevPacketBinary * packet = NULL;
if(input!=NULL)
{
switch(input->getVersion())
{
case cdevMessage::CDEV_PACKET_VERSION:
packet = encodePacket ((cdevMessage *)input);
break;
default:
packet = cdevSessionManager::encodePacket(input);
break;
}
if(serverInfo) serverInfo->sendPktCnt++;
}
return packet;
}
// *****************************************************************************
// * cdevServer::encodePacket :
// * This encodePacket method is designed specifically to postprocess the
// * data associated with a cdevMessage object.
// *****************************************************************************
cdevPacketBinary * cdevServer::encodePacket (cdevMessage * message )
{
cdevPacketBinary * result = NULL;
CLIPClientSession * client = NULL;
CLIPSocketSession * socket = NULL;
if(message!=NULL &&
message->getClientID()>0 &&
message->getTransIndex()>0 &&
(client = (CLIPClientSession *)findLocalClient(message->getClientID()))!=NULL &&
(socket = (CLIPSocketSession *)findSocket(client->getSocketID()))!=NULL)
{
char * binary = NULL;
size_t binaryLen = 0;
static cdevMessage outBound;
// *************************************************************
// * Remap the data to its foreign design.
// *************************************************************
if(message->getData()!=NULL)
socket->TagMap().localToRemote(*message->getData());
// *************************************************************
// * Transfer the critical data items into the class. Note that
// * we are not returning the deviceList, message, context,
// * cancelTransIndex, or operationCode with the return packet.
// *
// * Also note that the cdevData object is marked as permanent
// * and is using a pointer to the same cdevData object that is
// * in the message object.
// *************************************************************
outBound.clear();
outBound.setClientID (client->getClientID()&0xFFFF);
outBound.setTransIndex (message->getTransIndex());
outBound.setLocalDataIndex (message->getForeignDataIndex());
outBound.setForeignDataIndex (message->getLocalDataIndex());
outBound.setOperationCode (message->getOperationCode());
outBound.setCompletionCode (message->getCompletionCode());
outBound.setData (message->getData(), 1);
// *************************************************************
// * Create a binary stream from the cdevMessage object and then
// * place it into the outbound queue system.
// *************************************************************
outBound.streamOut(&binary, &binaryLen);
outBound.clear ();
result = new cdevPacketBinary;
result->attachData(binary, binaryLen);
// *************************************************************
// * Remap the data to its local design.
// *************************************************************
if(message->getData()!=NULL)
socket->TagMap().remoteToLocal(*message->getData());
}
return result;
}
+120
View File
@@ -0,0 +1,120 @@
#if !defined (_CDEV_SERVER_H_)
#define _CDEV_SERVER_H_
#include "SignalManager.h"
#include "cdevServerTools.h"
#include "cdevMessage.h"
#include "cdevContextMap.h"
#include "cdevTagMap.h"
#include "ClientInfo.h"
#include "ServerInfo.h"
#include "cdevGenericServerTags.h"
// *****************************************************************************
// * class CLIPClientSession :
// *
// * This class also allows the server to associate certain data with a
// * specific client...
// *
// * context: This is a pointer to the most recently used context from the
// * cdevContextMap.
// *****************************************************************************
class GENERIC_SERVER_API CLIPClientSession : public ClientSession
{
private:
cdevData *context;
public:
CLIPClientSession ( int SocketID, int ClientID, int LocalID )
: ClientSession(SocketID, ClientID, LocalID),
context(NULL)
{
}
virtual ~CLIPClientSession ( void ) { }
cdevData * getContext ( void ) { return context; }
void setContext ( cdevData * cxt ) { context = cxt; }
};
// *****************************************************************************
// * class CLIPSocketSession :
// *
// * This class allows the developer to associate additional information with
// * a particular socket...
// *
// * contextMap : a table of cdevData contexts that can be retrieved by index
// *
// * tagMap : the table for mapping cdevData tags from the remote system
// * to the tag table associated with this system.
// *****************************************************************************
class GENERIC_SERVER_API CLIPSocketSession : public SocketSession, public ClientInfoStruct
{
private:
cdevContextMap contextMap;
cdevTagMap tagMap;
public:
CLIPSocketSession ( int SocketID )
: SocketSession ( SocketID )
{
}
virtual ~CLIPSocketSession ( void ) { }
cdevContextMap & ContextMap ( void ) { return contextMap; }
cdevTagMap & TagMap ( void ) { return tagMap; }
};
// *****************************************************************************
// * cdevServer :
// * This is the cdevServer class. It is responsible for establishing a
// * listening socket, and then responding to all incoming and outgoing
// * messages.
// *****************************************************************************
class GENERIC_SERVER_API cdevServer : public cdevSessionManager
{
public:
typedef enum { SUCCESS = 0,
UNINITIALIZED,
CANT_OPEN_SOCKET,
CANT_REGISTER_LISTENER,
CANT_REGISTER_SERVER,
CANT_REGISTER_TIMER
} ServerInitStatus;
protected:
char * serverName;
class ClientAcceptor * acceptor;
cdevNameServerManager * timer;
ServerInitStatus status;
ServerInfo * serverInfo;
public:
static cdevGenericServerTagDef tags;
static SignalManager SigManager;
static sig_atomic_t Finished;
static void runServer (void);
cdevServer (char * DomainName, char * ServerName, unsigned short Port, double Rate);
cdevServer (void);
virtual ~cdevServer (void);
int startServer (char * DomainName, char * ServerName, unsigned short Port, double Rate, int searchForPort=0);
virtual void registerClient ( short localID );
virtual void unregisterClient ( short localID );
virtual ClientSession * newClientSession ( int SocketID, int ClientID, int LocalID );
virtual SocketSession * newSocketSession ( int SocketID );
virtual void deleteSocketSession ( SocketSession *socket );
virtual int dequeue (cdevMessage * &message);
void processLocal (cdevMessage * &message);
virtual cdevPacket * decodePacket (cdevPacketBinary * input);
virtual cdevPacket * decodePacket (cdevMessage * message);
virtual cdevPacketBinary * encodePacket (cdevPacket * input);
virtual cdevPacketBinary * encodePacket (cdevMessage * message);
virtual int operational (void) { return (status==0)?1:0; }
virtual ServerInitStatus getInitStatus(void) { return status; }
};
#endif /* _CDEV_SERVER_H_ */
+403
View File
@@ -0,0 +1,403 @@
#include "cdevServerTools.h"
// ****************************************************************************
// cdevSimpleTimer::cdevSimpleTimer :
// Constructor.
// ****************************************************************************
cdevSimpleTimer::cdevSimpleTimer ( cdevReactor & Reactor, double Rate)
{
setTimeoutRate(Rate);
if(Reactor.registerTimer (this) == -1)
{
outputError ( CDEV_SEVERITY_SEVERE, "cdevSimpleTimer",
"Unable to register timer with cdevReactor");
}
}
// ****************************************************************************
// cdevSimpleTimer::~cdevSimpleTimer :
// Destructor.
// ****************************************************************************
cdevSimpleTimer::~cdevSimpleTimer ( void )
{
if(reactor) reactor->extractHandler(this);
handleClose();
}
// ****************************************************************************
// cdevSimpleTimer::handle_timeout :
// Called when the timer expires.
// ****************************************************************************
int cdevSimpleTimer::handleTimeout(void)
{
return execute();
}
// *****************************************************************************
// * cdevNameServerManager::cdevNameServerManager :
// * This method will read the CDEV_NAME_SERVER environment variable to
// * obtain a list of one or more cdev name servers that should be notified
// * that this server is operational. The class will create a
// * cdevNameServerHandler event handler to manage the connection to each of
// * the specified name servers. If a connection is lost to one of the
// * name servers, the cdevNameServerManager will attempt periodically to
// * reconnect to the specific name server.
// *****************************************************************************
cdevNameServerManager::cdevNameServerManager ( cdevReactor & Reactor,
char * DomainName,
char * ServerName,
unsigned short ServerPort )
: domainName(strdup(DomainName)),
serverName(strdup(ServerName)),
serverPort(ServerPort),
nameServerList(NULL),
nameServerHandlers(NULL),
nameServerCnt(0)
{
// *********************************************************************
// * Prior to establishing any connections, the class must first install
// * all of the necessary data into the serverInfo and updateInfo
// * objects.
// *********************************************************************
char * userName = getenv("USER");
char hostname[255];
struct hostent * hostPtr;
struct timeval tv;
gettimeofday (&tv);
gethostname(hostname, 255);
hostPtr = gethostbyname(hostname);
serverInfo.insert ("name", serverName);
serverInfo.insert ("domain", domainName);
serverInfo.insert ("host", (char *)hostPtr->h_name);
serverInfo.insert ("owner", userName?userName:(char *)"UNKNOWN");
serverInfo.insert ("time", (long)tv.tv_sec);
serverInfo.insert ("port", (long)serverPort);
serverInfo.insert ("pid", getpid ());
updateInfo.insert ("name", serverName);
updateInfo.insert ("domain", domainName);
// *********************************************************************
// * First obtain a list of name servers from the CDEV_NAME_SERVER
// * environment variable. If this list is empty, then report an
// * error once and then never call the handler again.
// *********************************************************************
char * nsList = getenv("CDEV_NAME_SERVER");
int idx;
int length;
if(nsList!=NULL)
{
char * nsPtr = nsList;
char * endPtr = nsList;
// *************************************************************
// * Determine the number of name servers that have been
// * defined in the CDEV_NAME_SERVER environment variable.
// *************************************************************
while(*nsPtr==':' && *nsPtr!=0) nsPtr++;
if(*nsPtr!=0)
{
nameServerCnt = 1;
while(nsPtr!=NULL && *nsPtr!=0)
{
if((nsPtr = strchr(nsPtr, ':'))!=NULL)
{
while(*(++nsPtr)==':' && *nsPtr!=0);
if(*nsPtr!=0) nameServerCnt++;
}
}
}
// *************************************************************
// * Copy each of the defined cdev name server host names into
// * the nameServerList.
// *************************************************************
if(nameServerCnt>0)
{
nameServerList = new char *[nameServerCnt];
nameServerHandlers = new cdevNameServerHandler *[nameServerCnt];
for(nsPtr = nsList, idx=0; idx<nameServerCnt && *nsPtr!=0; idx++)
{
while(*nsPtr==':' && *nsPtr!=0) nsPtr++;
for(endPtr=nsPtr; *endPtr && *endPtr!=':'; endPtr++);
length = endPtr-nsPtr;
nameServerList[idx] = new char[length+1];
strncpy(nameServerList[idx], nsPtr, length);
nameServerList[idx][length] = 0;
nsPtr = endPtr;
}
}
}
// *********************************************************************
// * If no cdev name servers have been defined in the cdev name server
// * list, print a warning message.
// ********************************************************************
if(nameServerCnt<=0)
{
outputError(CDEV_SEVERITY_WARN, "CDEV Name Server Manager",
"\n%s\n%s\n%s",
" => No name servers have been specified in the CDEV_NAME_SERVER",
" => environment variable.",
" => This server WILL NOT be registered with any name servers...");
}
// *********************************************************************
// * Otherwise, set all of the cdevNameServerHandler pointers to NULL
// * so that they will be connected when the execute timeout method
// * is called.
// *********************************************************************
else {
for(idx=0; idx<nameServerCnt; idx++)
{
nameServerHandlers[idx] = NULL;
}
}
// *********************************************************************
// * Register this class with the cdevReactor so that it will be called
// * every thirty seconds. If any of the cdevNameServerHandlers have
// * become disconnected, it will attempt to reestablish the connection
// * at that time.
// *********************************************************************
setTimeoutRate(15.0);
Reactor.registerTimer(this);
handleTimeout();
}
// *****************************************************************************
// * cdevNameServerManager::~cdevNameServerManager :
// * This is the destructor for the cdevNameServerManager class. When called
// * it will remove and delete any cdevNameServerHandlers that have been
// * registered with the cdevReactor.
// *****************************************************************************
cdevNameServerManager::~cdevNameServerManager ( void )
{
int idx;
for(idx = 0; idx<nameServerCnt; idx++)
{
if(nameServerHandlers[idx]!=0) delete nameServerHandlers[idx];
if(nameServerList[idx]!=NULL) delete nameServerList[idx];
}
if(nameServerList!=NULL) delete nameServerList;
if(nameServerHandlers!=NULL) delete nameServerHandlers;
if(domainName!=NULL) delete domainName;
if(serverName!=NULL) delete serverName;
}
// *****************************************************************************
// * cdevNameServerManager::handleTimeout :
// * This method will be called every thirty seconds to make sure that all
// * connections to the cdev name servers are established. If a
// * cdevNameServerHandler has become disconnected, this method will attempt
// * to reconnect to the specified handler.
// *****************************************************************************
int cdevNameServerManager::handleTimeout ( void )
{
int idx;
for(idx=0; idx<nameServerCnt; idx++)
{
if(nameServerHandlers[idx]==NULL &&
nameServerList[idx]!=NULL)
{
int error = 0;
rsvcClient * client = new rsvcClient;
rsvcUdpClient * udpClient = new rsvcUdpClient;
if(client->connect(nameServerList[idx], RSVC_SERVER_PORT, 2.0)!=RSVC_SUCCESS)
{
outputError ( CDEV_SEVERITY_ERROR, "CDEV Name Server Manager",
"Failed to connect to name server on host %s",
nameServerList[idx]);
error = -1;
}
else if(udpClient->connect(nameServerList[idx], RSVC_SERVER_PORT+1024)!=RSVC_SUCCESS)
{
outputError ( CDEV_SEVERITY_ERROR, "CDEV Name Server Manager",
"Failed to open UDP port to name server on host %s",
nameServerList[idx]);
error = -1;
}
else if(client->insertValue ("cdevServers", serverInfo, rsvcCallback, nameServerList[idx], 1)!=RSVC_SUCCESS)
{
outputError ( CDEV_SEVERITY_ERROR, "CDEV Name Server Manager",
"Error transmitting to name server on host %s",
nameServerList[idx]);
error = -1;
}
if(!error) nameServerHandlers[idx] = new cdevNameServerHandler(*this, *reactor, idx, client, udpClient);
else {
client->disconnect();
udpClient->disconnect();
delete client;
delete udpClient;
}
}
}
return 0;
}
// *****************************************************************************
// * cdevNameServerManager::unregisterHandler:
// * This method is called whenever a cdevNameServerHandler needs to
// * unregister itself from the cdevNameServerManager. The handlers index
// * will be set to 0 and the connection will be reestablished the next time
// * the handleTimeout method is called.
// *****************************************************************************
void cdevNameServerManager::unregisterHandler ( size_t index )
{
if(index < nameServerCnt) nameServerHandlers[index] = NULL;
}
// *****************************************************************************
// * cdevNameServerManager::rsvcCallback :
// * This is the method that is called when the register server method
// * is executed.
// *****************************************************************************
void cdevNameServerManager::rsvcCallback (int status, void* arg, rsvcData* /* data */)
{
char * nameServer = (char *)arg;
if (status != RSVC_SUCCESS)
{
#ifndef _WIN32
fprintf(stderr, "CDEV Name Server Manager Error: %s %s\n",
"Failed to register with name server on host",
nameServer?nameServer:"UNKNOWN");
#endif
}
}
// *****************************************************************************
// * cdevNameServerHandler::cdevNameServerHandler :
// * This is the constructor for the cdevNameServerHandler class. It is
// * called by the cdevNameServerManager when a new cdevNameServerHandler
// * must be created to communicate with a name server. The connected
// * rsvcClient and rsvcUdpClient objects are provided to the class.
// ******************************************************************************
cdevNameServerHandler::cdevNameServerHandler ( cdevNameServerManager & Manager,
cdevReactor & Reactor,
int index,
rsvcClient * tcpClient,
rsvcUdpClient * udpClient )
: nameServerTCP(tcpClient), nameServerUDP(udpClient),
nameServerIndex(index), nameServerManager(Manager)
{
setTimeoutRate(5.0);
setMask(WRITE_MASK);
Reactor.registerHandler(this, WRITE_MASK);
Reactor.registerTimer (this);
}
// *****************************************************************************
// * cdevNameServerHandler::~cdevNameServerHandler :
// * This is the destructor for the class. this method is responsible for
// * disconnecting and closing the tcp and udp client connections and
// * sending notification to the cdevNameServerManager that it is
// * terminating.
// *****************************************************************************
cdevNameServerHandler::~cdevNameServerHandler ( void )
{
nameServerTCP->disconnect();
nameServerUDP->disconnect();
delete nameServerTCP;
delete nameServerUDP;
if(reactor) reactor->extractHandler(this);
nameServerManager.unregisterHandler(nameServerIndex);
}
// *****************************************************************************
// * cdevNameServerHandler::handleInput :
// * This method is called whenever data is ready to be read from the
// * nameServerTCP rsvcClient connection.
// *****************************************************************************
int cdevNameServerHandler::handleInput ( void )
{
return (nameServerTCP->pendIO()==RSVC_IOERROR)?-1:0;
}
// *****************************************************************************
// * cdevNameServerHandler::handleOutput :
// * This method is called whenever data is ready to be written to the
// * nameServerTCP rsvcClient connection.
// *****************************************************************************
int cdevNameServerHandler::handleOutput ( void )
{
setMask(READ_MASK);
return (nameServerTCP->pendIO()==RSVC_IOERROR)?-1:0;
}
// *****************************************************************************
// * cdevNameServerHandler::handleTimeout :
// * This method is called every five seconds to allow the object to emit
// * a UDP packet to the name server to notify it that the server is alive.
// *****************************************************************************
int cdevNameServerHandler::handleTimeout ( void )
{
if(nameServerUDP->update(*nameServerManager.getUpdateInfo())!=RSVC_SUCCESS)
{
outputError(CDEV_SEVERITY_ERROR, "CDEV Name Server Handler",
"Failed to update name server on host %s",
nameServerManager.getHostName(nameServerIndex));
}
return 0;
}
// *****************************************************************************
// * cdevNameServerHandler::getHandle :
// * This method allows the caller to obtain the file handle that is being
// * used by the nameServerTCP rsvcClient connection. This handle is used
// * by the cdevReactor to detect when an event has occured.
// *****************************************************************************
int cdevNameServerHandler::getHandle ( void ) const
{
return nameServerTCP->getFd();
}
#ifndef DONT_SUPPORT_CDEV_CALLS
// *********************************************************************
// * cdevSystemHandler::cdevSystemHandler :
// * This is the constructor for the cdevSystemHandler object.
// *********************************************************************
cdevSystemHandler::cdevSystemHandler (cdevReactor & Reactor,
double pollRate,
cdevSystem & System)
: cdevSimpleTimer(Reactor, pollRate), system(System)
{
execute();
}
#endif
#ifndef DONT_SUPPORT_CDEV_CALLS
// *********************************************************************
// * cdevSystemHandler::~cdevSystemHandler :
// * This is the destructor for the cdevSystemHandler object.
// *********************************************************************
cdevSystemHandler::~cdevSystemHandler (void)
{
}
#endif
#ifndef DONT_SUPPORT_CDEV_CALLS
// *********************************************************************
// * cdevSystemHandler::execute :
// * This is the method that is executed when the specified timer
// * expires.
// *********************************************************************
int cdevSystemHandler::execute (void)
{
system.poll();
return 0;
}
#endif
+113
View File
@@ -0,0 +1,113 @@
#if !defined (_CDEV_SERVER_TOOLS_H_)
#define _CDEV_SERVER_TOOLS_H_
#ifndef DONT_SUPPORT_CDEV_CALLS
#include "cdevSystemEventHandler.h"
#endif
#include "cdevSessionManager.h"
#include "cdevMessage.h"
#include "rsvcClient.h"
#include "rsvcUdpClient.h"
// *****************************************************************************
// * This is the cdevSimpleTimer class. The user specifies a function, a rate
// * and a void * data pointer. The ACE_Reactor will then call the function
// * each time the period of time expires.
// *****************************************************************************
class GENERIC_SERVER_API cdevSimpleTimer : public cdevEventHandler, public ErrorReporter
{
public:
cdevSimpleTimer ( cdevReactor & Reactor, double Rate = 1.0 );
~cdevSimpleTimer ( void );
virtual int handleTimeout (void);
virtual int execute (void) = 0;
};
// *****************************************************************************
// * class cdevNameServerManager:
// * This class will extract the list of cdev Name Servers from the
// * CDEV_NAME_SERVER environment variable and will create a collection
// * of cdevNameServerHandler objects that will manage the connections
// * to them. These cdevNameServerHandler objects will register the
// * server with the name server and will send updates every five seconds.
// * If the connection to the specific name server is lost, the
// * cdevNameServer handler will call the unregisterHandler method to
// * terminate the connection and before being deleted. This object
// * will then attempt to reestablish the connection periodically.
// *****************************************************************************
class GENERIC_SERVER_API cdevNameServerManager : public cdevEventHandler, public ErrorReporter
{
protected:
char ** nameServerList;
class cdevNameServerHandler ** nameServerHandlers;
size_t nameServerCnt;
rsvcData serverInfo;
rsvcData updateInfo;
char * serverName;
char * domainName;
unsigned short serverPort;
public:
cdevNameServerManager ( cdevReactor & Reactor, char * DomainName, char * ServerName, unsigned short port);
~cdevNameServerManager ( void );
static void rsvcCallback (int status, void* arg, rsvcData* data);
virtual int handleTimeout ( void );
void unregisterHandler ( size_t index );
rsvcData * getServerInfo ( void ) { return &serverInfo; }
rsvcData * getUpdateInfo ( void ) { return &updateInfo; }
char * getHostName ( size_t index ) { return (index<nameServerCnt)?nameServerList[index]:(char *)"UNKNOWN"; }
};
// *****************************************************************************
// * class cdevNameServerHandler :
// * This class will establish a connection with a specific cdev Name
// * Server and will send period updates to let the name server know that
// * the process is still alive and operational. If the handler loses its
// * connection to the name server, it will notify the cdevNameServerManager
// * prior to being deleted. The cdevNameServer manager will then attempt
// * to recreate the connection at a later time.
// *****************************************************************************
class GENERIC_SERVER_API cdevNameServerHandler : public cdevEventHandler, public ErrorReporter
{
protected:
int nameServerIndex;
rsvcClient * nameServerTCP;
rsvcUdpClient * nameServerUDP;
cdevNameServerManager & nameServerManager;
public:
cdevNameServerHandler ( cdevNameServerManager & Manager, cdevReactor & Reactor, int index, rsvcClient *tcp, rsvcUdpClient *udp );
~cdevNameServerHandler ( void );
virtual int handleInput ( void );
virtual int handleOutput ( void );
virtual int handleTimeout ( void );
virtual int getHandle ( void ) const;
};
#ifndef DONT_SUPPORT_CDEV_CALLS
// *********************************************************************
// * class cdevSystemHandler:
// * This class causes the specified cdevReactor to periodically
// * poll the cdevSystem for activity (by default 5 times per second).
// *********************************************************************
class GENERIC_SERVER_API cdevSystemHandler : public cdevSimpleTimer
{
protected:
cdevSystem & system;
public:
cdevSystemHandler (cdevReactor & Reactor,
double pollRate = 0.2,
cdevSystem & System = cdevSystem::defaultSystem());
virtual ~cdevSystemHandler ( void );
int execute ( void );
};
#endif
#endif /* _CDEV_SERVER_TOOLS_H_ */
@@ -0,0 +1,635 @@
#include <cdevSessionManager.h>
cdevReactor cdevSessionManager::Reactor;
IntHash cdevSessionManager::localIdx;
// *****************************************************************************
// * ClientSession::ClientSession :
// * This method serves only to initialize the internals of the class.
// *****************************************************************************
ClientSession::ClientSession ( int SocketID, int ClientID, int LocalID )
: socketID(SocketID), clientID(ClientID), localID(LocalID)
{
}
// *****************************************************************************
// * ClientSession::~ClientSession :
// * This method deletes all unprocessed binary packets that are stored
// * within the queue.
// *****************************************************************************
ClientSession::~ClientSession ( void )
{
}
// *****************************************************************************
// * SocketSession::SocketSession :
// * This is the constructor for the SocketSession object, it serves only
// * to initialize internal variables.
// *****************************************************************************
SocketSession::SocketSession( int SocketID )
: FifoQueue(), socketID(SocketID)
{
}
// *****************************************************************************
// * SocketSession::~SocketSession :
// * This method deletes all unprocessed binary packets that are stored
// * within the queue.
// *****************************************************************************
SocketSession::~SocketSession ( void )
{
char * binary;
size_t binaryLen;
while(dequeue(&binary, &binaryLen)==0) delete binary;
}
// *****************************************************************************
// * cdevSessionManager::~cdevSessionManager :
// * This method deletes all entries from the client and socket queues
// * and then deletes all queue objects...
// *****************************************************************************
cdevSessionManager::~cdevSessionManager ( void )
{
int socketID;
int clientID;
IntHashIterator clientIter(&clients);
IntHashIterator socketIter(&sockets);
ClientSession * clientPtr;
SocketSession * socketPtr;
cdevPacketBinary * packet;
while((packet = (cdevPacketBinary *)inbound.dequeue())!=NULL)
{
delete packet;
}
clientIter.first();
while((clientPtr=(ClientSession *)clientIter.data())!=NULL)
{
clientID = clientPtr->getClientID();
clientIter++;
removeClient(clientID, 0);
}
socketIter.first();
while((socketPtr=(SocketSession *)socketIter.data())!=NULL)
{
socketID = socketIter.key();
socketIter++;
removeSocket(socketID);
}
}
// *****************************************************************************
// * cdevSessionManager::newClientSession :
// * This method allows the caller to create a new ClientSession object. The
// * ClientSession object functions primarily as a pointer to the queue that
// * holds packets destined for a specific client, however, the developer
// * can create a subclass of the ClientSession that may be used to associate
// * additional, client specific information to the structure.
// *****************************************************************************
ClientSession * cdevSessionManager::newClientSession ( int SocketID, int ClientID, int LocalID )
{
return new ClientSession (SocketID, ClientID, LocalID);
}
// *****************************************************************************
// * cdevSessionManager::newSocketSession :
// * This method allows the caller to create a new SocketSession object. The
// * SocketSession object functions primarily as a pointer to the queue that
// * holds packets destined for a specific socket, however, the developer
// * can create a subclass of the SocketSession that may be used to associate
// * additional, socket specific information to the structure.
// *****************************************************************************
SocketSession * cdevSessionManager::newSocketSession ( int SocketID )
{
return new SocketSession (SocketID);
}
// *****************************************************************************
// * cdevSessionManager::deleteSocketSession :
// * This method is called to delete a SocketSession object.
// *****************************************************************************
void cdevSessionManager::deleteSocketSession ( SocketSession *socket )
{
if(socket)
{
sockets.remove(socket->getSocketID());
delete socket;
}
}
// *****************************************************************************
// * cdevSessionManager::findLocalClient :
// * This method allows the caller to locate a ClientSession using the local
// * client identifier that is assigned by the cdevSessionManager class.
// *****************************************************************************
ClientSession * cdevSessionManager::findLocalClient( short localID )
{
return (ClientSession *)localIdx.find(localID);
}
// *****************************************************************************
// * cdevSessionManager::findClient :
// * This method allows the caller to locate a ClientSession using its
// * clientID.
// *****************************************************************************
ClientSession * cdevSessionManager::findClient( int clientID )
{
return (ClientSession *)clients.find(clientID);
}
// *****************************************************************************
// * cdevSessionManager::findSocket :
// * This method allows the caller to locate a SocketSession using its
// * socketID.
// *****************************************************************************
SocketSession * cdevSessionManager::findSocket( int socketID )
{
return (SocketSession *)sockets.find(socketID);
}
// *****************************************************************************
// * cdevSessionManager::addClient :
// * This method allows the caller to add a new clientID and construct a
// * ClientSession object for it.
// *
// * The socketID must have already been registered using the addSocket
// * method. If the clientID already exists or if an error occurs NULL will
// * be returned.
// *****************************************************************************
ClientSession * cdevSessionManager::addClient( int socketID, int clientID )
{
ClientSession * session = NULL;
if(findSocket(socketID)!=NULL && findClient(clientID)==NULL)
{
short localID = getNextLocalID();
if(localID>0 &&
(session = newClientSession(socketID, clientID, localID))!=NULL)
{
clients.insert (clientID, session);
localIdx.insert((int)localID, session);
}
}
return session;
}
// *****************************************************************************
// * cdevSessionManager::addSocket :
// * This method allows the caller to add a new socketID and construct a
// * SocketSession object to service it.
// *
// * This function will fail if the socketID has already been registered...
// * On failure this method will return NULL.
// *****************************************************************************
SocketSession * cdevSessionManager::addSocket ( int socketID )
{
SocketSession * session = NULL;
if(findSocket(socketID)==NULL &&
(session = newSocketSession(socketID))!=NULL)
{
sockets.insert(socketID, session);
}
return session;
}
// *****************************************************************************
// * cdevSessionManager::removeClient :
// * This method will remove the specified clientID from the clients list
// * and will delete the associated ClientSession object.
// *****************************************************************************
void cdevSessionManager::removeClient ( int clientID, int unregisterFlag)
{
ClientSession * session;
if((session = (ClientSession *)clients.find(clientID))!=NULL)
{
// *****************************************************
// * Submit an unregister command to notify the server
// *****************************************************
if(unregisterFlag) unregisterClient(session->getLocalID());
localIdx.remove((int)session->getLocalID());
clients.remove (clientID);
delete session;
}
}
// *****************************************************************************
// * cdevSessionManager::removeSocket :
// * This method will remove the specified socketID from the sockets list
// * and will delete the associated SocketSession object. It will then
// * ascend the clients list and will remove all ClientSessions that are
// * associated with the socketID.
// *****************************************************************************
void cdevSessionManager::removeSocket ( int socketID )
{
cdevEventHandler * handler = NULL;
SocketSession * socket;
ClientSession * client;
int clientID;
if(Reactor.getHandler(socketID, handler)==0 && handler!=NULL)
{
delete handler;
}
if((socket = (SocketSession *)sockets.find(socketID))!=NULL)
{
IntHashIterator clientIter(&clients);
clientIter.first();
while((client=(ClientSession *)clientIter.data())!=NULL)
{
if(client->getSocketID()==socketID)
{
clientID = client->getClientID();
clientIter++;
removeClient(clientID);
}
else clientIter++;
}
deleteSocketSession(socket);
}
}
// *****************************************************************************
// * cdevSessionManager::getNextLocalID :
// * This method allows the caller to retrieve a unique localID to be
// * assigned to a client. The nextLocalID value is automatically
// * incremented.
// *****************************************************************************
short cdevSessionManager::getNextLocalID ( void )
{
static short nextLocalID = 0;
short startingPoint = nextLocalID;
ClientSession *session = NULL;
if(nextLocalID>=32767) nextLocalID = 1;
else nextLocalID++;
startingPoint = nextLocalID;
do {
session=(ClientSession *)localIdx.find((int)nextLocalID);
if(session!=NULL)
{
if(nextLocalID>=32767) nextLocalID = 1;
else nextLocalID++;
}
} while(session!=NULL && nextLocalID!=startingPoint);
return session==NULL?nextLocalID:-1;
}
// *****************************************************************************
// * cdevSessionManager::enqueue :
// * This method is used to enqueue a binary packet into the inbound
// * fifoQueue. This method is called by the client handler objects.
// *
// * The binary data item becomes the property of the queue and should not
// * be accessed later by the caller.
// *****************************************************************************
int cdevSessionManager::enqueue( int socketID, char * binary, unsigned binaryLen )
{
int result = -1;
SocketSession * socket = NULL;
ClientSession * client = NULL;
cdevPacketBinary * packet = new cdevPacketBinary;
// *********************************************************************
// * Make sure its a valid packet.
// *********************************************************************
if(packet->streamIn(binary, binaryLen)==0)
{
// *************************************************************
// * Add the socketID if it does not already exist.
// *************************************************************
if((socket = findSocket(socketID))==NULL)
{
socket = addSocket(socketID);
}
// *************************************************************
// * Combine the (short)clientID and the (short)socketID to
// * create a unique identifier for this client.
// *
// * < HIGH WORD > < LOW WORD >
// * SSSSSSSS CCCCCCCCC
// *************************************************************
short packetClientID;
int clientID;
packet->getClientID(packetClientID);
clientID = ((socketID<<16)|packetClientID);
// *************************************************************
// * Add a clientID if it does not already exist.
// *************************************************************
if((client = findClient(clientID))==NULL &&
(client = addClient(socketID, clientID))!=NULL)
{
registerClient ( client->getLocalID() );
}
// *************************************************************
// * This would only fail if the clientID had already been used
// * by another socket.
// *************************************************************
if(client!=NULL && client->getSocketID()==socketID)
{
result = 0;
// *****************************************************
// * At this point everything necessary is known to
// * submit the packet for processing...
// *****************************************************
// *****************************************************
// * Set the clientID to the localClientID to be used
// * by the server side of the connection.
// *****************************************************
packet->setClientID(client->getLocalID());
// *****************************************************
// * Enqueue the packet and set the pointer to NULL to
// * prevent its later deletion.
// *****************************************************
inbound.enqueue((void *)packet);
packet = NULL;
}
}
if(packet!=NULL) delete packet;
// *********************************************************************
// * If a packet was successfully added to the inbound queue and the
// * queue is not empty , then add an event to the FDTrigger to cause
// * the handle_input mechanism to be called.
// *********************************************************************
if(result==0 && !inbound.empty()) trigger.insertEvent();
// *********************************************************************
// * Due to the new design of the SocketUtil class, the binary should
// * never be deleted.
// *********************************************************************
return result;
}
// *****************************************************************************
// * cdevSessionManager::enqueue :
// * This method is used to enqueue a cdevPacket packet into an outbound
// * queue. The method will first identify the target client and place the
// * packet into its queue... then it will identify the socket and place the
// * packet into its queue.
// *
// * This method will return -1 if either the socket or the client is
// * undefined.
// *
// * The packet remains the property of the caller who must delete it.
// *****************************************************************************
int cdevSessionManager::enqueue ( cdevPacket * input )
{
cdevPacketBinary * packet = encodePacket(input);
ClientSession * client = NULL;
SocketSession * socket = NULL;
int result = -1;
// *********************************************************************
// * Note that this condition makes sure that a queue exists for both
// * the client ID and the socketID. If both of these conditions are
// * met, then it checks to ensure that the socket has not been overrun
// * with data (500 or more packets).
// *********************************************************************
if(packet!=NULL &&
input->getClientID()>0 &&
(client = findLocalClient(input->getClientID()))!=NULL &&
(socket = findSocket(client->getSocketID()))!=NULL)
{
cdevEventHandler * handler = NULL;
Reactor.getHandler(client->getSocketID(), handler);
// *************************************************************
// * Attempt to flush the handler if more than 500 packets
// * have already been inserted.
// *************************************************************
if(handler && socket->getCount()>=500)
{
outputError(CDEV_SEVERITY_WARN, "CDEV Server",
"Forcing flush of socket %i to prevent overflow...",
client->getSocketID());
// *****************************************************
// * Call handleOutput to cause the handler to attempt
// * to write its contents.
// *****************************************************
if(handler->handleOutput()<0)
{
Reactor.removeHandler(handler);
handler = NULL;
}
}
if(handler && socket->getCount()<500)
{
char * binary = NULL;
size_t binaryLen = 0;
// *****************************************************
// * Create a binary stream from the cdevPacketBinary
// * object and then use the detachData method to
// * prevent the buffer from being deleted when the
// * cdevPacketBinary is destroyed.
// *****************************************************
packet->streamOut(&binary, &binaryLen);
packet->detachData();
// *****************************************************
// * Populate the cdevServerBinary object with the data
// * that was extracted from the cdevPacketBinary object
// *****************************************************
socket->enqueue(binary, binaryLen);
// *****************************************************
// * Set the event mask for the outbound socket to read/
// * write to force it to attempt to write to the socket
// * until data is transmitted.
// *****************************************************
if(handler) handler->setMask(READ_MASK|WRITE_MASK);
result = 0;
}
else if(handler)
{
outputError ( CDEV_SEVERITY_ERROR, "CDEV Server",
"Dropping packet to socket %i : queue is full...",
client->getSocketID());
}
}
// *********************************************************************
// * Delete the cdevPacketBinary if it was generated.
// *********************************************************************
if(packet) delete packet;
return result;
}
// *****************************************************************************
// * cdevSessionManager::dequeue :
// * This method is used to by the server engine to extract a message from
// * the inbound queue. Once the packet has been enqueue it will be sent to
// * the decodePacket method which will perform any preprocessing that the
// * developer may deem necessary before returning the packet to the caller.
// *
// * The cdevPacket object becomes the property of the caller and must
// * be deleted when it is no longer needed.
// *****************************************************************************
int cdevSessionManager::dequeue ( cdevPacket * &packet )
{
cdevPacketBinary * binary = NULL;
packet = NULL;
// *********************************************************************
// * Conitnue this loop unitl a valid packet has been extracted, or
// * until there are no more binary packets left in the queue.
// *********************************************************************
do {
// *************************************************************
// * Attempt to dequeue the cdevPacketBinary from the queue...
// * If it is not NULL, then begin processing.
// *************************************************************
if((binary = (cdevPacketBinary *)inbound.dequeue())!=NULL)
{
// *****************************************************
// * Call the decodePacket mechanism... This will use
// * the factory mechanisms of the cdevPacket class to
// * import the data and then will complete any required
// * preprocessing...
// *****************************************************
packet = decodePacket(binary);
// *****************************************************
// * Delete the binary as it is no longer needed.
// *****************************************************
delete binary;
}
} while(binary!=NULL && packet==NULL);
// *********************************************************************
// * Return 0 if a cdevPacket was successfully dequeued, otherwise,
// * return -1.
// *********************************************************************
return packet?0:-1;
}
// *****************************************************************************
// * cdevServer::decodePacket :
// * This method is used to perform preprocessing on a newly dequeued binary
// * packet before it is provided to the caller. This method allows the
// * developer to perform special preparations on the packet before providing
// * it to the caller.
// *****************************************************************************
cdevPacket * cdevSessionManager::decodePacket (cdevPacketBinary * input)
{
return input?cdevPacket::import(*input):(cdevPacket *)NULL;
}
// *****************************************************************************
// * cdevSessionManager::encodePacket :
// * This method is used to perform postprocessing on a packet that has been
// * enqueued to be sent to a client. This method allows the developer to
// * perform special preparations on the packet before providing it to the
// * client.
// *****************************************************************************
cdevPacketBinary * cdevSessionManager::encodePacket ( cdevPacket * input )
{
cdevPacketBinary *result;
if(input)
{
char * binary;
size_t binaryLen;
input->streamOut(&binary, &binaryLen);
if(binary && binaryLen)
{
result = new cdevPacketBinary;
result->attachData(binary, binaryLen);
}
}
return result;
}
// *****************************************************************************
// * cdevSessionManager::getHandle :
// * This method is used to obtain a copy of the file handle that is used
// * to poll for events. In this case it will be the read file descriptor
// * of the FD_Trigger object.
// *****************************************************************************
int cdevSessionManager::getHandle ( void ) const
{
return trigger.readfd();
}
// *****************************************************************************
// * cdevSessionManager::handleInput :
// * This method is called whenever there is a read event pending on the
// * FD_Trigger object.
// *****************************************************************************
int cdevSessionManager::handleInput ( void )
{
processMessages();
if(inbound.empty()) trigger.purge();
return 0;
}
// ****************************************************************************
// cdevSessionManager::handleClose :
// Shuts down the timer - this will result in the destruction of the
// * cdevSessionManager object.
// ****************************************************************************
int cdevSessionManager::handleClose(void)
{
return -1;
}
// ****************************************************************************
// * cdevSessionManager::handleTimeout :
// * Called when the timer expires... This method serves only to call the
// * processMessages function.
// ****************************************************************************
int cdevSessionManager::handleTimeout(void)
{
processMessages();
if(inbound.empty()) trigger.purge();
return 0;
}
// ***************************************************************************
// * cdevServer::processMessages :
// * This is a simple stand-in function that retrieves a message from the
// * queue and then immediately returns it to the outbound queue. This
// * function should be overloaded by the developers mechanism for
// * processing messages.
// ***************************************************************************
void cdevSessionManager::processMessages ( void )
{
cdevPacket * packet;
while(dequeue(packet)==0)
{
enqueue(packet);
delete packet;
}
}
@@ -0,0 +1,222 @@
#if !defined (_CDEV_SESSION_MANAGER_H_)
#define _CDEV_SESSION_MANAGER_H_
#include "cdevReactor.h"
#include "cdevEventHandler.h"
#include "cdevAddr.h"
#include "cdevSocketStream.h"
#include "ErrorReporter.h"
#include "FD_Trigger.h"
#include "fifo.h"
#include "IntHash.h"
#include "cdevPacket.h"
// *****************************************************************************
// * class ClientSession :
// *
// * The ClientSession allows the server to associate certain data
// * with a specific client...
// *
// * The ClientSession object may be sub-classed later in order to allow the
// * developer to attach additional information to a particular client id.
// *
// * localID : This is the short integer that uniquely identifies the
// * client on the server side of the connection.
// *
// * clientID : This is an integer that uniquely identifies the client
// * within the context of the socket... That is to say...
// *
// * The high-order short integer is the socketID - AND -
// * the low-order short integer is a clientID that uniquely
// * identifies the client on the client side of the connection.
// *
// * Even though both the clientID and the localID uniquely
// * identify the client... both are maintained in order to
// * perform bi-directional routing of packets between multiple
// * repeaters.
// *
// * socketID : This is the socket number that the client is attached on.
// *
// * context: This is a pointer to the most recently used context from the
// * cdevContextMap.
// *****************************************************************************
class GENERIC_SERVER_API ClientSession
{
private:
short localID;
int clientID;
int socketID;
public:
ClientSession ( int SocketID, int ClientID, int LocalID );
virtual ~ClientSession ( void );
int getSocketID ( void ) { return socketID; }
void setSocketID ( int SocketID ) { socketID = SocketID; }
int getClientID ( void ) { return clientID; }
void setClientID ( int ClientID ) { clientID = ClientID; }
short getLocalID ( void ) { return localID; }
void setLocalID ( short LocalID ) { localID = LocalID; }
};
// *****************************************************************************
// * class SocketSession :
// * This class is a queue that will feed to a specific socket in that is
// * connected to a remote client. The class contains the following variables.
// *
// * The SocketSession object may be sub-classed later in order to allow the
// * developer to attach additional information to a particular socket.
// *
// * socketID : the integer identifier of the socket
// *
// * contextMap : a table of cdevData contexts that can be retrieved by index
// *
// * tagMap : the table for mapping cdevData tags from the remote system
// * to the tag table associated with this system.
// *****************************************************************************
class GENERIC_SERVER_API SocketSession : public FifoQueue
{
private:
int socketID;
public:
SocketSession ( int SocketID );
virtual ~SocketSession ( void );
int getSocketID ( void ) { return socketID; }
void setSocketID ( int SocketID ) { socketID = SocketID; }
};
// *****************************************************************************
// * class cdevSessionManager:
// * The cdevSessionManager class is used to manage the queues that are
// * associated with the operation of a server. It maintains the following
// * variables.
// *
// * Reactor : This is the cdevReactor that will be used to manage the client
// * connections.
// *
// * localIdx : This is the next available local index. This value will be
// * used to populate the localID variable in each ClientSession.
// *
// *
// * trigger : This is the file descriptor mechanism that is used to
// * notify the cdevEventHandler mechanisms when a packet
// * is available to be dequeued.
// *
// * rate : This is the rate at which the handleTimeout mechanism will
// * be periodically called in order to process time oriented
// * events.
// *
// * inbound : This is the inbound message queue. Since all messages will
// * go to the same method to be processed, they can be enqueued
// * in the same queue for storage.
// *
// * clients : This is a hash table that contains the queues for each
// * client that is attached to the server. Since multiple
// * clients can be routed through the same socket, these
// * classes are used to determine which socket to send the
// * outbound message to.
// *
// * sockets : This is the hash table that contains the queues for each
// * socket. After the cdevSessionManager determines the clientID
// * associated with an outbound message, that value will be used
// * to locate the SocketSession - where the outbound message will
// * be enqueued.
// *****************************************************************************
class GENERIC_SERVER_API cdevSessionManager : public ErrorReporter, public cdevEventHandler
{
public:
static cdevReactor Reactor;
protected:
static IntHash localIdx;
FD_Trigger trigger;
FifoQueue inbound;
IntHash clients;
IntHash sockets;
public:
cdevSessionManager ( void ) {}
virtual ~cdevSessionManager ( void );
static short getNextLocalID ( void );
// *********************************************************************
// * These mechanisms are used to obtain new ClientSession and
// * SocketSession objects. This design allows the developer to affix
// * addition socket specific or client specific components to his
// * classes later in order to have an easily accessable data point.
// *********************************************************************
virtual ClientSession * newClientSession ( int SocketID, int ClientID, int LocalID );
virtual SocketSession * newSocketSession ( int SocketID );
virtual void deleteSocketSession ( SocketSession * session );
// *********************************************************************
// * These mechanisms are used to add, locate and remove client and
// * socket sessions.
// *********************************************************************
virtual ClientSession * findLocalClient ( short localID );
virtual ClientSession * findClient ( int clientID );
virtual SocketSession * findSocket ( int socketID );
virtual ClientSession * addClient ( int socketID, int clientID );
virtual SocketSession * addSocket ( int socketID );
virtual void removeClient ( int clientID, int unregisterFlag=1);
virtual void removeSocket ( int socketID );
// *********************************************************************
// * This mechanism is used by the ClientHandler class to enqueue binary
// * information incoming from the socket.
// *********************************************************************
virtual int enqueue ( int socketID, char * binary, unsigned binaryLen );
// *********************************************************************
// * This method is used by the developer to send a packet to a specific
// * client. The client is specified by the clientID in the cdevPacket.
// *********************************************************************
virtual int enqueue ( cdevPacket * packet );
// *********************************************************************
// * This method is used by the developer to extract a packet from the
// * inbound queue for processing.
// *********************************************************************
int dequeue ( cdevPacket * &packet );
// *********************************************************************
// * This method is used by the developer to dequeue, process and then
// * enqueue messages to a client.
// *********************************************************************
virtual void processMessages ( void );
// *********************************************************************
// * This method formats packets when they are being enqueued or
// * dequeued.
// *********************************************************************
virtual cdevPacket * decodePacket (cdevPacketBinary * input);
virtual cdevPacketBinary * encodePacket (cdevPacket * input);
// *********************************************************************
// * These are the ACE_Event_Handler methods that are called whenever
// * a timeout, input or output event occur.
// *********************************************************************
virtual int getHandle (void) const;
virtual int handleInput (void);
virtual int handleClose (void);
virtual int handleTimeout (void);
// *********************************************************************
// * These are developer defined functions which are used to announce
// * the creation of a new client or the destruction of an existing
// * client to the main processing loop.
// *
// * Typically, they will only enqueue a message in the server specific
// * packet format that specifies either "register" or "unregister" and
// * the local client identifier.
// *********************************************************************
virtual void registerClient ( short localID ) = 0;
virtual void unregisterClient ( short localID ) = 0;
};
#endif /* _CDEV_SESSION_MANAGER_H_ */
@@ -0,0 +1,90 @@
#include "cdevSystem.h"
#include "cdevSessionManager.h"
class GENERIC_SERVER_API cdevSystemEventHandler : public cdevEventHandler
{
private:
int cdevfd;
public:
cdevSystemEventHandler ( cdevReactor & Reactor, int fd )
: cdevfd(fd)
{
Reactor.registerHandler(this, READ_MASK|WRITE_MASK|EXCEPT_MASK);
}
~cdevSystemEventHandler ( void )
{
if(reactor) reactor->extractHandler(this);
handleClose();
}
int handleInput ( void )
{
cdevSystem::defaultSystem().poll();
return 0;
}
int handleOutput ( void )
{
cdevSystem::defaultSystem().flush();
return 0;
}
int handleExcept ( void )
{
cdevSystem::defaultSystem().poll();
return 0;
}
int getHandle ( void ) const
{
return cdevfd;
}
};
class GENERIC_SERVER_API cdevSystemEventManager : public cdevEventHandler
{
public:
cdevSystemEventManager ( cdevReactor &Reactor=cdevSessionManager::Reactor, double Rate = 30.0 )
{
setTimeoutRate(Rate);
Reactor.registerTimer(this);
cdevSystem::defaultSystem().addFdChangedCallback
(cdevSystemEventManager::fdChangedCallback, this);
}
~cdevSystemEventManager ( void )
{
if(reactor) reactor->extractHandler(this);
handleClose();
}
int handleTimeout ( void )
{
cdevSystem::defaultSystem().pend(0.0001);
return 0;
}
static void fdChangedCallback ( int fd, int opened, void * arg )
{
cdevSystemEventManager *manager = (cdevSystemEventManager *)arg;
cdevEventHandler *handler;
cdevReactor *reactor = manager->reactor;
if(reactor)
{
if(opened && reactor->getHandler(fd, handler)!=0)
{
handler = new cdevSystemEventHandler(*reactor, fd);
}
else if(!opened && reactor->getHandler(fd, handler)==0)
{
delete handler;
}
}
}
};