The implementation of a server requires a class derived from
CServer2
. This is the active object base class
responsible for handling the asynchronous requests from the client program.
An instance of the CServer2
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, ultimately,
have more than one active object, then it may be important for the
CServer2
active object to have the highest priority.
The server can now be started. This is a code fragment taken from the
example that can be found at
...\examples\Base\IPC\ClientServer\complex
.
CCountServServer *pS=new CCountServServer(EPriorityStandard);
__ASSERT_ALWAYS(pS!=NULL,CCountServServer::PanicServer(ESvrCreateServer));
...
// Start the server
TInt err = pS->Start(KCountServerName);
if (err != KErrNone)
{
CCountServServer::PanicServer(ESvrStartServer);
}
The function CServer2::Start()
adds the
CServer2
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 CServer2::RunL()
protected member function.
A request for a connection by a client thread results in the creation
of a new session. The request for a connection results in a call by the
client/server framework to the CServer2::NewSessionL()
function. A derived class must provide an implementation - creating and
initialising an instance of a CSession2
derived class. The
framework takes this newly created session object to the server's queue.
For a non sharable session, requests for disconnection by a client
thread cause the relevant CSession2
object to be deleted. The
CSession2
destructor should perform appropriate cleanup.
Any other message is passed to CSession2::ServiceL()
.
This function must be implemented by a derived class.
The base class CSession2
represents a client's session on
the server side. This class provides the standard session behaviour. A class
derived from CSession2
must be defined and implemented. The
following class definition, taken from the example that can be found at:
...\examples\Base\IPC\ClientServer\simple
, is typical:
class CCountServSession : public CSession2
{
public:
CCountServSession();
//service request
void ServiceL(const RMessage2& aMessage);
void DispatchMessageL(const RMessage2& aMessage);
// services available to initialize/increase/decrease/reset and
// return the counter value.
void SetFromStringL(const RMessage2& aMessage);
void Increase();
void Decrease();
void IncreaseBy(const RMessage2& aMessage);
void DecreaseBy(const RMessage2& aMessage);
void CounterValue(const RMessage2& aMessage);
void Reset();
protected:
// panic the client
void PanicClient(const RMessage2& aMessage,TInt aPanic) const;
private:
TInt iCount;
};
Note the following:
The function ServiceL()
is called by the client/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. This is simply a mechanism to delegate the handling of different
request types.
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 therefore needs to read data from the client address space.
ServiceL()
This is implemented as follows:
void CCountServSession::ServiceL(const RMessage2& 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.
DispatchMessageL()
This is implemented as follows:
void CCountServSession::DispatchMessageL(const RMessage2& aMessage)
{
switch (aMessage.Function())
{
case ECountServSetFromString:
SetFromStringL(aMessage);
return;
case ECountServIncrease:
Increase();
return;
case ECountServIncreaseBy:
IncreaseBy(aMessage);
return;
...
case ECountServValue:
CounterValue(aMessage);
return;
...
default:
PanicClient(aMessage,EBadRequest);
return;
}
}
IncreaseBy()
This message service function is implemented as follows:
void CCountServSession::IncreaseBy(const RMessage2& aMessage)
{
iCount = iCount + aMessage.Int0();
}
Note that we need to pass the message object to the function. The
Int0()
function is used to get the integer specified in the client
call - the '0' on the end of the function name indicates that the integer is
the first parameter in the set passed across from the client.
SetFromStringL()
This message service function is implemented as follows:
void CCountServSession::SetFromStringL(const RMessage2& aMessage)
{
// length of passed descriptor (1st parameter passed from client)
TInt deslen = aMessage.GetDesLength(0);
// Passed data will be saved in this descriptor.
RBuf buffer;
// Max length set to the value of "deslen", but current length is zero
buffer.CreateL(deslen);
// Do the right cleanup if anything subsequently goes wrong
buffer.CleanupClosePushL();
// Copy the client's descriptor data into our buffer.
aMessage.ReadL(0,buffer,0);
// Now do a validation to make sure that the string only has digits
if (buffer.Length() == 0)
{
User::Leave(ENonNumericString);
}
...
// Do rest of work to convert from
// string to integer, and assign.
RMessage::ReadL()
reads the descriptor from 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. A basic test is done
to make sure there data is supplied.
CounterValue()
This is implemented as follows:
void CCountServSession::CounterValue(const RMessage2& aMessage)
{
TPckgBuf<TInt> p(iCount);
aMessage.WriteL(0,p);
}
It writes data back to a descriptor in the client address space. The corresponding client request is:
TInt RCountServSession::CounterValue()
{
TInt res=0;
TckgBuf<TInt> pckg;
// Note that TPckgBuf is of type TDes8
TIpcArgs args(&pckg);
SendReceive(ECountServValue, args);
// Extract the value returned from the server.
res = pckg();
return res;
}
The TInt
is packaged into a descriptor before
being passed to the server. The packaging mechanism is known as package buffer.
The write operation copies the descriptor, i.e. the package
buffer containing the integer value, back to the descriptor in the client
address space. Note that the zero specified in
aMessage.WriteL(0,p);
means that the argument referred to is the
first in the list passed across from the client side via the
TIpcArgs
object.