The implementation of a server requires a class derived from
CServer
. This is the active object base class
responsible for handling the asynchronous requests from the client
program.
An instance of the CServer
derived class is,
typically, created by the server's thread function. As an active object, it
needs a priority and this is passed as a parameter to the constructor. The
choice of priority value depends on the server's design. If the server can have
more than one active object, then it may be important for the
CServer
active object to have the highest priority.
The server can now be started.
void CCountServServer::New()
{
CCountServServer *pS=new CCountServServer(EPriority);
__ASSERT_ALWAYS(pS!=NULL,PanicServer(ESvrCreateServer));
TInt r=pS->Start(KCountServerName);
__ASSERT_ALWAYS(r==KErrNone,PanicServer(ESvrStartServer));
}
CServer::Start()
adds the CServer
active object to the active scheduler and issues the first request for
messages. The server is now waiting for messages.
As with all active objects, the completion of requests for
messages is handled by the CServer::RunL()
protected member
function.
Note that in ER5, the server name must be given its name as a step in the construction process. It is important this be done before starting the server otherwise an E32USER-CBase 55 panic is raised. The name is created in a heap descriptor and the pointer to this heap descriptor assigned to the public data member CServer::iName. The function New() in the above code fragment becomes:
void CCountServServer::New()
{
CCountServServer *pS=new CCountServServer(EPriority);
__ASSERT_ALWAYS(pS!=NULL,PanicServer(ESvrCreateServer));
HBufC *pN=(&KCountServerName)->Alloc();
__ASSERT_ALWAYS(pN!=NULL,PanicServer(ESvrCreateServer));
pS->iName=pN;
TInt r=pS->Start();
__ASSERT_ALWAYS(r==KErrNone,PanicServer(ESvrStartServer));
}
Requests for connection by a client thread result in the creation of
a new session. A new CSharableSession
object is constructed,
initialised and added to the server’s session queue.
For a non sharable session, requests for disconnection by a client
thread cause the relevant CSharableSession
object to be deleted.
The CSharableSession
destructor should perform appropriate
cleanup.
Any other message is passed to
CSharableSession::ServiceL()
. This function must be implemented by
a derived class.
The base class CSharableSession
represents a client's
session on the server side. This class provides the standard session behaviour.
The CSession
class derived from CSharableSession
adds
the concept of the client thread. A class derived from CSession
must be defined and implemented. The following class definition, taken from
example code, is typical:
class CCountServSession : public CSession
{
public:
CCountServSession(RThread &aClient, CCountServServer * aServer);
static CCountServSession* NewL(RThread &aClient, CCountServServer* aServer);
virtual void ServiceL(const RMessage &aMessage);
void DispatchMessageL(const RMessage &aMessage);
void SetFromStringL();
void Increase();
void Decrease();
void IncreaseBy();
void DecreaseBy();
void CounterValue();
void Reset();
protected:
void PanicClient(TInt aPanic) const;
void Write(const TAny* aPtr,const TDesC8& aDes,TInt anOffset=0);
private:
CCountServServer* iCountSvr;
TInt iCount;
};
Note the following:
The function ServiceL()
is called by the server
framework to handle all messages except requests to connect and
disconnect.
ServiceL()
calls DispatchMessageL()
under
a trap harness.
DispatchMessageL()
determines the appropriate message
service function to call by examining the operation code of the current
message.
The class provides message service functions:
Increase()
, IncreaseBy()
etc to service specific
messages from clients.
The function SetFromStringL()
needs a string specified
by the client and reads the data from the client address space.
The function Write()
is used to write information to
the client address space.
ServiceL()
This is implemented as follows:
void CCountServSession::ServiceL(const RMessage& aMessage)
{
TRAPD(err,DispatchMessageL(aMessage));
aMessage.Complete(err);
}
After calling the appropriate service function via
DispatchMessageL()
, the asynchronous request is completed with
aMessage.Complete()
which passes the completion code back to the
client. Failing to do this will hang the server.
DispatchMessageL()
This is implemented as follows:
void CCountServSession::DispatchMessageL(const RMessage &aMessage)
{
switch (aMessage.Function())
{
case ECountServSetFromString:
SetFromStringL();
return;
case ECountServIncrease:
Increase();
return;
case ECountServIncreaseBy:
IncreaseBy();
return;
case ECountServValue:
CounterValue();
return;
...............
default:
PanicClient(EBadRequest);
return;
}
}
IncreaseBy()
This message service function is implemented as follows:
void CCountServSession::IncreaseBy()
{
iCount = iCount + Message().Int0();
}
the function Message().Int0()
is used to obtain the
integer specified in the client call.
SetFromStringL()
This message service function is implemented as follows:
void CCountServSession::SetFromStringL()
{
TInt res;
const TAny* pD=Message().Ptr0();
TInt desLen=Message().Client().GetDesLength(pD);
HBufC8* writeBuf=HBufC8::New(desLen);
TPtr initptr = writeBuf->Des();
TRAP(res,Message().ReadL(pD,initptr));
if (res!=KErrNone)
PanicClient(EBadDescriptor);
.......................
// Do rest of work to convert from string to integer, and assign.
}
RMessage::ReadL()
reads the contents of the client
address space as specified by the first argument in the message and copies the
data into the descriptor specified as its second argument. The result is tested
and the server panics the client if the read fails.
Write()
This is implemented as follows:
void CCountServSession::Write(const TAny* aPtr,const TDesC8& aDes,TInt anOffset)
{
TRAPD(ret,WriteL(aPtr,aDes,anOffset);)
if (ret!=KErrNone)
PanicClient(EBadDescriptor);
}
If the write fails the server panics the client. The
PanicClient()
function calls
CSession::Panic()
.
CounterValue()
uses this function to write the current
counter value to the client address specified in
RCountServ::CounterValue()
and returned by
Message().Ptr0()
:
void CCountServSession::CounterValue()
{
TPckgBuf<TInt> p(iCount);
Write(Message().Ptr0(),p);
}
Arguments must be packaged before they can be read from or written to the client.