Provides code snippets to help you to implement a simple server interface.
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.
Construction and initialisation
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.
Handling requests
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; }
Notes
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.