Symbian
Symbian OS Library

SYMBIAN OS V9.3

[Index] [Spacer] [Previous] [Next]



How to implement a simple server interface


Handling asynchronous requests

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.

[Top]


Server side session representation

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:


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