Symbian
Symbian Developer Library

SYMBIAN OS V9.4

Feedback

[Index] [Previous] [Next]


Client server example code

[Top]


SimpleServer: simple server


Example code

These files are found in: examples\Base\IPC\ClientServer\Simple

The files reproduced here are the main files contained in the examples directory. Some extra files may be needed to run the examples, and these will be found in the appropriate examples directory.

// ClientServer.h
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.
//
#include <e32base.h>


// server name

_LIT(KCountServerName,"CountServer");

// A version must be specifyed when creating a session with the server

const TUint KCountServMajorVersionNumber=0;
const TUint KCountServMinorVersionNumber=1;
const TUint KCountServBuildVersionNumber=1;

IMPORT_C TInt StartThread(RThread& aServerThread);


// Function codes (opcodes) used in message passing between client and server
enum TCountServRqst
    {
    ECountServCreate = 1,
    ECountServSetFromString,
    ECountServClose,
    ECountServUnsupportedRequest,
    ECountServIncrease,
    ECountServIncreaseBy,
    ECountServDecrease,
    ECountServDecreaseBy,
    ECountServValue,
    ECountServReset
    };

enum TCountServLeave
{
    ENonNumericString = 99
};
// SimpleServer.h
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.

#include <e32base.h>

// needed for creating server thread.
const TUint KDefaultHeapSize=0x10000;

// reasons for server panic
enum TCountServPanic
    {
    EBadRequest = 1,
    EBadDescriptor,
    EMainSchedulerError,
    ESvrCreateServer,
    ESvrStartServer,
    ECreateTrapCleanup,
    ENotImplementedYet,
    };


//**********************************
//CCountServServer
//**********************************
/**
Our server class - an active object - and therefore derived ultimately from CActive.
It accepts requests from client threads and forwards
them to the client session to be dealt with. It also handles the creation
of the server-side client session.
*/
class CCountServServer : public CServer2
    {
public:
      // Creates a new session with the server; the function
      // implements the pure virtutal function 
      // defined in class CServer2
    CSession2* NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const;
public :
      // The thread function executed by the server
    static TInt ThreadFunction(TAny* aStarted);
      // Function to panic the server
    static void PanicServer(TCountServPanic aPanic);

protected:
    CCountServServer(CActive::TPriority aActiveObjectPriority);
    };


//**********************************
//CCountServSession
//**********************************
/**
This class represents a session with the  server.
Functions are provided to respond appropriately to client messages.
*/
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;
    };
// SimpleServer.cpp
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.

// The implementation of our simple server that increments and decrements a simple counter value.


//
//**NOTE**: The example does not demonstrate any security features - its purpose is simply
//          to demonstrate the basic principles of client/server interaction.
//


#include "ClientServer.h"
#include "SimpleServer.h"
#include <e32svr.h>
#include <e32uid.h>


//**********************************
//CCountServServer - implementations
//**********************************

/**
Constructor takes the server priority value. 

The server is an active object, and the priority value is the priority
of this active object.

It passes the priority value to the base class in the Ctor list.
By default, the session is not sharable, which is what we want here
so no second parameter is passed to the CServer2 constructor.
*/
CCountServServer::CCountServServer(CActive::TPriority aActiveObjectPriority)
    : CServer2(aActiveObjectPriority)
    {
    }


/**
Creates a new session with the server.
*/
CSession2* CCountServServer::NewSessionL(const TVersion& aVersion,const RMessage2& /*aMessage*/) const
    {
      // Check that the version is OK
    TVersion v(KCountServMajorVersionNumber,KCountServMinorVersionNumber,KCountServBuildVersionNumber);
    if (!User::QueryVersionSupported(v,aVersion))
        User::Leave(KErrNotSupported);  
    
    // CAN USE THE aMessage argument to check client's security and identity
    // can make use of this later but for now ignore. AH 4/5/05
    // the connect message is delivered via the RMessage2 object passed. 
    
    // do something with this later (and move it to the start of the function?)
    
      // Create the session.
    return new (ELeave) CCountServSession;
    }


/**
A utility function to panic the server.
*/
void CCountServServer::PanicServer(TCountServPanic aPanic)
    {
    _LIT(KTxtServerPanic,"Count server panic");
    User::Panic(KTxtServerPanic,aPanic);
    }


//***********************************
//CCountServSession - implementations
//***********************************


/**
Constructor
*/
CCountServSession::CCountServSession()
    {
    }

/**
Services a client request.
*/
void CCountServSession::ServiceL(const RMessage2& aMessage)
    {
    TRAPD(err,DispatchMessageL(aMessage));
    aMessage.Complete(err);
    }

/**
Called by ServiceL()

It tests the function code and then delegates to
the appropriate function.
*/
void CCountServSession::DispatchMessageL(const RMessage2& aMessage)
    {
    switch (aMessage.Function())
        {
    case ECountServSetFromString:
        SetFromStringL(aMessage);
        return;
    case ECountServIncrease:
        Increase();
        return;
    case ECountServIncreaseBy:
        IncreaseBy(aMessage);
        return;
    case ECountServDecrease:
        Decrease();
        return;
    case ECountServDecreaseBy:
        DecreaseBy(aMessage);
        return;
    case ECountServReset:
        Reset();
        return;
    case ECountServValue:
        CounterValue(aMessage);
        return;
      
      // This is an example of a request that we know about, but don't support.
      // We cause KErrNotSupported to be returned to the client.
    case ECountServUnsupportedRequest:
        User::Leave(KErrNotSupported);
        
     //  Requests that we don't understand at all are a different matter.
     //  This is considered a client programming error, so we panic the 
     //  client - this also completes the message.
    default:
        PanicClient(aMessage,EBadRequest);
        return;
        }
    }

/**
Initialize the counter with the numeric equivalent of the descriptor contents
This function is here to demonstrate reading from the client address space.
Note that in this example, the client and the server are part of the same process,
*/
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);
        }
    
    TLex16 lexer;
    
    lexer.Assign(buffer);
    while (!lexer.Eos())
        {
        TChar thechar;
        
        thechar = lexer.Peek();
        if (!thechar.IsDigit())
            {
            User::Leave(ENonNumericString);
            }
        lexer.Inc();
        }
       
      // Convert to a simple TInt value. 
    lexer.Assign(buffer);           
    if (lexer.Val(iCount))
        {
        User::Leave(ENonNumericString);
        }
        
      // Clean up the memory acquired by the RBuf variable "buffer"
    CleanupStack::PopAndDestroy();
    }


/**
Increases the session counter by default (1)
*/
void CCountServSession::Increase()
    {
    iCount++;
    }


/**
Increases the session counter by an integer.
*/
void CCountServSession::IncreaseBy(const RMessage2& aMessage)
    {
    iCount = iCount + aMessage.Int0();
    }

/**
Decreases the session counter by default (1)
*/
void CCountServSession::Decrease()
    {
    iCount--;
    }


/**
Decreases the session counter by an integer.
*/
void CCountServSession::DecreaseBy(const RMessage2& aMessage)
    {
    iCount = iCount - aMessage.Int0();  
    }


/**
Resets the session counter.
*/
void CCountServSession::Reset()
    {
    iCount=0;
    }


/**
Writes the counter value to a descriptor in the client address space.
This function demonstrates writing to the client.
*/
void CCountServSession::CounterValue(const RMessage2& aMessage)
    {
    TPckgBuf<TInt> p(iCount);
    aMessage.WriteL(0,p);
    }


/**
Panics the client
*/
void CCountServSession::PanicClient(const RMessage2& aMessage,TInt aPanic) const
    {
    _LIT(KTxtServer,"CountServ server");
    aMessage.Panic(KTxtServer,aPanic);
    }



//**********************************
//Global functions
//**********************************

// The count server thread function that initialises the server.
GLDEF_C TInt CCountServServer::ThreadFunction(TAny* /**aStarted*/)
    {
      // get clean-up stack
    CTrapCleanup* cleanup=CTrapCleanup::New();
    if (cleanup == NULL)
        {
        CCountServServer::PanicServer(ECreateTrapCleanup);
        }
    
      // create an active scheduler and server
    CActiveScheduler *pA=new CActiveScheduler;
    __ASSERT_ALWAYS(pA!=NULL,CCountServServer::PanicServer(EMainSchedulerError));
    CCountServServer *pS=new CCountServServer(EPriorityStandard);
    __ASSERT_ALWAYS(pS!=NULL,CCountServServer::PanicServer(ESvrCreateServer));
        
      //Install the active scheduler
    CActiveScheduler::Install(pA);
      
      // Start the server
    TInt err = pS->Start(KCountServerName);
    if (err != KErrNone)
        {
        CCountServServer::PanicServer(ESvrStartServer);
        }
    
      // Let everyone know that we are ready to
      // deal with requests.
    RThread::Rendezvous(KErrNone);
    
      // And start fielding requests from client(s).
    CActiveScheduler::Start();

      // Tidy up...    
    delete pS;
    delete pA;
    delete cleanup; 
    
      // ...although we should never get here!
    return(KErrNone);
    }


/**
Create the thread that will act as the server.
This function is exported from the DLL and called by the client.

Note that a server can also be implemented as a separate
executable (i.e. as a separate process).
*/
EXPORT_C TInt StartThread(RThread& aServerThread)
    {
    TInt res=KErrNone;
    
      // Create the server, if one with this name does not already exist.
    TFindServer findCountServer(KCountServerName);
    TFullName   name;
    
      // Need to check that the server exists.
    if (findCountServer.Next(name)!=KErrNone)
        {
          // Create the thread for the server.
       res=aServerThread.Create(KCountServerName,
            CCountServServer::ThreadFunction,
            KDefaultStackSize,
            KDefaultHeapSize,
            KDefaultHeapSize,
            NULL
            );
            
          // The thread has been created OK so get it started - however
          // we need to make sure that it has started before we continue.
        if (res==KErrNone)
            {
            TRequestStatus rendezvousStatus;
            
            aServerThread.SetPriority(EPriorityNormal);
            aServerThread.Rendezvous(rendezvousStatus);
            aServerThread.Resume();
            User::WaitForRequest(rendezvousStatus);
            }
            
          // The thread has not been created - clearly there's been a problem.
        else
            {
            aServerThread.Close();
            }
        }
    return res;
    }
    
    
// SimpleServer.mmp
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.

// using relative paths for source and userinclude directories

// exports are unfrozen

TARGET        SimpleServer.dll
TARGETTYPE    dll
UID           0
VENDORID 0x70000001

SOURCEPATH    .
SOURCE        SimpleServer.cpp

USERINCLUDE   .
SYSTEMINCLUDE \Epoc32\include

LIBRARY       euser.lib

CAPABILITY    all


#if defined(WINS)
    deffile .\SimpleServerWINS.def
#else if defined(ARM)
    deffile .\SimpleServerARM.def
#endif

nostrictdef

See SimpleClient.

[Top]


SimpleClient: simple client


Example code

These files are found in: examples\Base\IPC\ClientServer\Simple

The files reproduced here are the main files contained in the examples directory. Some extra files may be needed to run the examples, and these will be found in the appropriate examples directory.

/ SimpleClient.cpp
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.


//
//**NOTE**: The example does not demonstrate any security features - its purpose is simply
//        to demonstrate the basic principles of client/server interaction.
//



// needed for client interface
#include "ClientServer.h"

// needed for client (doExampleL)
#include "SimpleClient.h"
#include "CommonFramework.h"


const TUint kDefaultMessageSlots=4;


//**********************************
//RCountServ
//**********************************

RCountServSession::RCountServSession()
    {
    }


/**
Connects to the  server using 4 message slots.

In this example, the server is implemented as a separate thread.
The function starts that thread before attempting to create a session with the server.

The version information specifies the earliest version of the server that we can
talk to.
*/
TInt RCountServSession::Connect()
    {
    TInt r=StartThread(iServerThread);
    if (r==KErrNone)
        r=CreateSession(KCountServerName,Version(),kDefaultMessageSlots);
    return(r); 
    }


/**
Returns the earliest version number of the server that we can tal to.
*/  
TVersion RCountServSession::Version(void) const
    {
    return(TVersion(KCountServMajorVersionNumber,KCountServMinorVersionNumber,KCountServBuildVersionNumber));
    }


/**
A server request to set the counter value 
to the value defined in the string.
*/
TInt RCountServSession::SetFromString(const TDesC& aString)
    {
    TIpcArgs args(&aString);
    return SendReceive(ECountServSetFromString, args);
    }


/**
A server request to increase the counter value by the default value (i.e. 1)
*/
void RCountServSession::Increase()
    {
    SendReceive(ECountServIncrease);
    }


/**
A server request to increase the counter value by the specified
integer value.
*/
void RCountServSession::IncreaseBy(TInt anInt)
    {
    TIpcArgs args(anInt);
    SendReceive(ECountServIncreaseBy, args);
    }


/**
A server request to decrease the counter value by the default value (i.e. 1).
*/
void RCountServSession::Decrease()
    {
    SendReceive(ECountServDecrease);    
    }


/**
A server request to decrease the counter value by the specified
integer value.
*/
void RCountServSession::DecreaseBy(TInt anInt)
    {
    TIpcArgs args(anInt);
    SendReceive(ECountServDecreaseBy, args);
    }

/**
A server request to reset the counter value to 0.
*/
void RCountServSession::Reset()
    {
    SendReceive(ECountServReset);   
    }


/**
A server request to get the curent value of the counter.

We pass a TPckgBuf across to the server.
*/
TInt RCountServSession::CounterValue()
    {
    TInt res=0;
    TPckgBuf<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;
    }


/**
A server request to stop the server.

This is a request that is NOT implemented by the server; it is here to show
the handling of non-implemented requests.
*/
TInt RCountServSession::UnsupportedRequest()
    {
    return SendReceive(ECountServUnsupportedRequest);
    }

/**
A request that the server knows nothing about.
*/
void RCountServSession::BadRequest()
    {
    SendReceive(9999);
    }



/**
Closing the server and tidying up.
*/
void RCountServSession::Close()
    {
    RSessionBase::Close();
    iServerThread.Close();  
    }




/**
This is the main body of the example. It connects to the server,
causing it to be created if it doesn't already exist. 

It continues by making various requests on the server, displaying the results of those 
requests. 
*/
LOCAL_C void doExampleL()
    {
    _LIT(KTxtTestingCountServer,"Testing the count server \n\n");
    _LIT(KTxtInitCounterWith,"\nInitialize the counter with : ");        
    _LIT(KTxtInitCounterFailed,"\nSetting the counter from string failed: non-numeric character detected\n"); 
    _LIT(KTxtGetCounterValue,"\nGetting the counter value back from the server: %d \n");
    
    
      // Say that we are testing the count server
    console->Printf(KTxtTestingCountServer); 
    
      // This is our handle to the server
    RCountServSession ss;

      // Connect to the count server, starting it up if we need to 
      // (which in this example we do need to).
      // This creates a session with the server.
    User::LeaveIfError(ss.Connect());

      // At this point the server appears ready to accept requests.
    console->Printf(KTxtInitCounterWith);

      // Initialise the counter by passing an illegal string - this should
      // fail - but we will check anyway before reporting a failure.
    _LIT(KTxtIllegalString,"22h4");
    console->Printf(KTxtIllegalString);
    TInt ret = ss.SetFromString(KTxtIllegalString);
    if (ret==ENonNumericString)
        {
        console->Printf(KTxtInitCounterFailed);
        }
    
      // Now try and initialise the counter using a legal string.
    console->Printf(KTxtInitCounterWith);
    _LIT(KTxtLegalString,"224");
    console->Printf(KTxtLegalString);
    ret = ss.SetFromString(KTxtLegalString);
    if (ret==ENonNumericString)
        {
        console->Printf(KTxtInitCounterFailed);
        }
    
      // Now get the value back from the server, just to see what it believes
      // it has. We are expecting the value 224 (of course!!!).
    ret = ss.CounterValue();
    console->Printf(KTxtGetCounterValue, ret);
    
      // Now increase the counter value by the default value.
    _LIT(KTxt1,"\nIncrease counter (default 1)..");        
    console->Printf(KTxt1);
    ss.Increase();
    ret = ss.CounterValue();
    console->Printf(KTxtGetCounterValue, ret);

      // Now increase the counter value by 2.
    _LIT(KTxt2,"\nIncrease counter by 2..");       
    console->Printf(KTxt2);
    ss.IncreaseBy(2);
    ret = ss.CounterValue();
    console->Printf(KTxtGetCounterValue, ret);
    
      // Now decrease the counter value by the default value.    
    _LIT(KTxt3,"\nDecrease counter(default 1)..");      
    console->Printf(KTxt3);
    ss.Decrease();
    ret = ss.CounterValue();
    console->Printf(KTxtGetCounterValue, ret);
    
      // Now increase the counter value by 7.
    _LIT(KTxt4,"\nIncrease counter by 7..");       
    console->Printf(KTxt4);
    ss.IncreaseBy(7);
    ret = ss.CounterValue();
    console->Printf(KTxtGetCounterValue, ret);
        
      // Now increase the counter value again by the default value.     
    _LIT(KTxt5,"\nIncrease counter(default 1)..");  
    console->Printf(KTxt5);
    ss.Increase();
    ret = ss.CounterValue();
    console->Printf(KTxtGetCounterValue, ret);

      // Now decrease the counter value by 3.      
    _LIT(KTxt6,"\nDecrease counter by 3..");   
    console->Printf(KTxt6);
    ss.DecreaseBy(3);
    ret = ss.CounterValue();
    console->Printf(KTxtGetCounterValue, ret);
    
      // Reset counter value
    _LIT(KTxt7,"\nReseting counter value to 0..");
    console->Printf(KTxt7);
    ss.Reset();
    ret = ss.CounterValue();
    console->Printf(KTxtGetCounterValue, ret);

    
      // Call API function which is not implemented in the server
    _LIT(KTxt8,"\nAbout to call the unsupported function Stop()..");
    console->Printf(KTxt8);
    ret = ss.UnsupportedRequest();
    _LIT(KTxt9,"\nSorry, UnsupportedRequest() is not supported\n");
    if (ret==KErrNotSupported)
        {
        console->Printf(KTxt9);
        }
          
     
      // This request will panic this client so do not remove the
      // following comment unless you want to try it.
//  ss.BadRequest();
   
   
      // Close the sesssion with the count server.
    ss.Close();
    }
// SimpleClient.h
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.
//
#if !defined(__COUNTSERV_H__)
#define __COUNTSERV_H__

#if !defined(__E32BASE_H__)
#include <e32base.h>
#endif


//**********************************
//RCountServSession
//**********************************

// Our client-side handle to a session with the server.
// The class forms a layer over the Symbian provided RSessionBase class.
//
// The class deals with the requests made by the main example code by forwarding
// them to the server. The type of request is identified by a code, one of
// the TCountServRqst enum values, and arguments are passed via a TIpcArgs object.
//
// The functions Increase(), Decrease() etc are wrappers for different calls to 
// SendReceive().
//
// Most of the functions here return void; if they fail, the server panics the client.
// If they return it can be assumed that there is no error.
//
// The RThread object is a handle to the thread that is acting as the server.


class RCountServSession : public RSessionBase
    {
public:
    RCountServSession();
    TInt Connect();
    TVersion Version() const;
    TInt UnsupportedRequest();
    TInt SetFromString(const TDesC& aString);
    void Increase();
    void Decrease();
    void IncreaseBy(TInt anInt);
    void DecreaseBy(TInt anInt);
    void Reset();
    TInt CounterValue();
    void BadRequest();
    void Close();
private:
    RThread iServerThread;
    };


#endif
// ClientServer.h
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.
//
#include <e32base.h>


// server name

_LIT(KCountServerName,"CountServer");

// A version must be specifyed when creating a session with the server

const TUint KCountServMajorVersionNumber=0;
const TUint KCountServMinorVersionNumber=1;
const TUint KCountServBuildVersionNumber=1;

IMPORT_C TInt StartThread(RThread& aServerThread);


// Function codes (opcodes) used in message passing between client and server
enum TCountServRqst
    {
    ECountServCreate = 1,
    ECountServSetFromString,
    ECountServClose,
    ECountServUnsupportedRequest,
    ECountServIncrease,
    ECountServIncreaseBy,
    ECountServDecrease,
    ECountServDecreaseBy,
    ECountServValue,
    ECountServReset
    };

enum TCountServLeave
{
    ENonNumericString = 99
};

The file "commonFramework.h" is found in examples/Base/CommonFramework

#ifndef __EUSTD_H
#define __EUSTD_H

#include <e32base.h>
#include <e32cons.h>

_LIT(KTxtEPOC32EX,"EPOC32EX");
_LIT(KTxtExampleCode,"E32 SDK Example Code");
_LIT(KFormatFailed,"failed: leave code=%d");
_LIT(KTxtOK,"ok");
_LIT(KTxtPressAnyKey," [press any key]");

// public
LOCAL_D CConsoleBase* console; // write all your messages to this
LOCAL_C void doExampleL(); // code this function for the real example

// private
LOCAL_C void callExampleL(); // initialize with cleanup stack, then do example

GLDEF_C TInt E32Main() // main function called by E32
    {
    __UHEAP_MARK;
    CTrapCleanup* cleanup=CTrapCleanup::New(); // get clean-up stack
    TRAPD(error,callExampleL()); // more initialization, then do example
    __ASSERT_ALWAYS(!error,User::Panic(KTxtEPOC32EX,error));
    delete cleanup; // destroy clean-up stack
    __UHEAP_MARKEND;
    return 0; // and return
    }

LOCAL_C void callExampleL() // initialize and call example code under cleanup stack
    {
    console=Console::NewL(KTxtExampleCode,TSize(KConsFullScreen,KConsFullScreen));
    CleanupStack::PushL(console);
    TRAPD(error,doExampleL()); // perform example function
    if (error)
        console->Printf(KFormatFailed, error);
    else
        console->Printf(KTxtOK);
    console->Printf(KTxtPressAnyKey);
    console->Getch(); // get and ignore character
    CleanupStack::PopAndDestroy(); // close console
    }

#endif
// SimpleClient.mmp
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.

// using relative paths for source and userinclude directories

TARGET        SimpleClient.exe
TARGETTYPE    exe
UID           0
VENDORID 0x70000001

SOURCEPATH    .
SOURCE        SimpleClient.cpp

USERINCLUDE   .
USERINCLUDE   ..\..\..\CommonFramework
SYSTEMINCLUDE \Epoc32\include

LIBRARY       euser.lib  SimpleServer.lib
// BLD.INF
// Component description file 
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.

PRJ_MMPFILES

SimpleServer.mmp
SimpleClient.mmp

Description

This pair of examples shows a simple use of the client server interface. The client, SimpleClient, has a single session with the server SimpleServer, implemented as a statically linked DLL.


Build Notes

The server must be built before the client. This requirement is specified in the component description file, bld.inf. SimpleServer.mmp is listed before SimpleClient.mmp. Build activities relating to particular .mmp files are carried out in the order in which they are listed in the bld.inf file.


Classes used

[Top]


ComplexServer: server supporting multiple subsessions


Example code

Found in: examples\Base\IPC\ClientServer\Complex

The files reproduced here are the main files contained in the examples directory. Some extra files may be needed to run the examples, and these will be found in the appropriate examples directory.

// ComplexServer.cpp
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.

#include "ComplexClientAndServer.h"
#include "ComplexServer.h"
#include <e32svr.h>
#include <e32uid.h>

//
//NOTE**: The example does not demonstrate any security features - its purpose is simply
//        to demonstrate the basic principles of client/server interaction.
//


/**
The count server thread function that initialises the server.
*/
TInt CCountServer::ThreadFunction(TAny* /**aStarted*/)
    {
      // Useful TInt variable
    TInt err;
    
      // create cleanup stack
    CTrapCleanup* cleanup = CTrapCleanup::New();
    if (cleanup == NULL)
        {
        PanicServer(ECreateTrapCleanup);
        }
        
      // Create an active scheduler.
    CActiveScheduler* pScheduler=new CActiveScheduler;
    __ASSERT_ALWAYS(pScheduler,PanicServer(EMainSchedulerError));
      // Install the active scheduler.
    CActiveScheduler::Install(pScheduler);
    
      // Create the server object.
    CCountServer* pServer = NULL;
    TRAP(err,pServer = CCountServer::NewL(EPriorityStandard));
    __ASSERT_ALWAYS(!err,CCountServer::PanicServer(ESvrCreateServer));
    
      // Start the server
    err = pServer->Start(KCountServerName);
    if (err != KErrNone)
        {
        CCountServer::PanicServer(ESvrStartServer);
        }
        
      // Let everyone know that we are ready to
      // deal with requests.
    RThread::Rendezvous(KErrNone);
    
      // And start fielding requests from client(s).
    CActiveScheduler::Start();
    
      // Tidy up...
    delete pServer;
    delete pScheduler;
    delete cleanup; 

      // ...although we should never get here!
    return KErrNone;
    }


/**
Create the thread that will act as the server.
This function is exported from the DLL and called by the client.

Note that a server can also be implemented as a separate
executable (i.e. as a separate process).
*/
EXPORT_C TInt StartThread(RThread& aServerThread)
    {
    TInt res=KErrNone;
    
      // Create the server, if one with this name does not already exist.
    
    TFindServer findCountServer(KCountServerName);
    TFullName   name;
    
      // Need to check that the server exists.
    if (findCountServer.Next(name)!=KErrNone)
        {
          // Create the thread for the server.
        res=aServerThread.Create(KCountServerName,
            CCountServer::ThreadFunction,
            KDefaultStackSize,
            KDefaultHeapSize,
            KDefaultHeapSize,
            NULL
            );
          // The thread has been created OK so get it started - however
          // we need to make sure that it has started before we continue.
        if (res==KErrNone)
            {
            TRequestStatus rendezvousStatus;
            
            aServerThread.SetPriority(EPriorityNormal);
            aServerThread.Rendezvous(rendezvousStatus);
            aServerThread.Resume();
            User::WaitForRequest(rendezvousStatus);
            }
            
          // The thread has not been created - clearly there's been a problem.
        else
            {
            aServerThread.Close();
            }
        }
    return res;
    }
// ComplexServer.h
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.

#if !defined(__ComplexServer_H__)
#define __ComplexServer_H__

//needed for creating server thread.
const TUint KDefaultHeapSize=0x10000;

// panic reasons
enum TCountServPanic
    {
    EBadRequest = 1,
    EBadDescriptor,
    EDescriptorNonNumeric,
    EMainSchedulerError,
    ESvrCreateServer,
    ESvrStartServer,
    ECreateTrapCleanup,
    EBadCounterRemove, 
    EBadSubsessionHandle 
    };




/*
CCountServer class

Represents the server.
    
The server starts with the first client connect call.
Start includes setting up active scheduler, the server active object,
and the object container index which produces object object containers for each session.
*/
class CCountServer : public CServer2
    {
public:
    

      // Creates a new session with the server; the function
      // implements the pure virtutal function 
      // defined in class CServer2
    CSession2* NewSessionL(const TVersion &aVersion,const RMessage2& aMessage) const;

public: 
      // Creats a new server object
    static CCountServer* NewL(CActive::TPriority aActiveObjectPriority);
    
      // The thread function executed by the server 
    static TInt ThreadFunction(TAny* aStarted);

      // utility function to panic the server.
    static void PanicServer(TCountServPanic aPanic);

public :
      // Constructor
    CCountServer(CActive::TPriority aActiveObjectPriority);
    
      // Second phase constructor
    void ConstructL();

      // Returns an object container, and guaranteed 
      // to produce object containers with unique
      // ids within the server.
      // Called by a new session to create a container 
    CObjectCon* NewContainerL();
    
      // Destructor; exists to do some tidying up.
    ~CCountServer();

private:
      // The server has an object container index that
      // creates an object container for each session.
    CObjectConIx* iContainerIndex; 
    };




/*
CCountSession class

Represents a session with the server.
    
Functions are provided to respond appropriately to client messages.
A session can own any number of subsession objects.
*/
class CCountSubSession;
class CCountSession : public CSession2
    {
public:
      // Create the session
    static CCountSession* NewL();
    
public:
      // Constructor
    CCountSession();
    
      // Called by client/server framework after 
      // session has been successfully created
    void CreateL(); 
        
      // Service request
    void ServiceL(const RMessage2& aMessage);
    void DispatchMessageL(const RMessage2& aMessage);

      // Creates new subsession
    void NewCounterL(const RMessage2& aMessage);  
      
      // Closes the session
    void CloseSession();
    
      // Gets the number of resources (i.e. CCountSubSession objects)
    void NumResources(const RMessage2& aMessage);
    
      // Utility to return the CCountSubSession (subsession) object
    CCountSubSession* CounterFromHandle(const RMessage2& aMessage,TInt aHandle);    

      // Delete the subsession object through its handle.
    void DeleteCounter(TInt aHandle);
      
      // Gets the number of server-side subsession objects.
    TInt CountResources();
      
      // Panics client
    void PanicClient(const RMessage2& aMessage,TInt aPanic) const;

private:
      // Object container for this session.
    CObjectCon *iContainer;

      // Object index which stores objects
      // (CCountSubSession instances) for this session.
    CObjectIx* iCountersObjectIndex;

      // Total number of resources. In this example
      // a resource is just the number of CCountSubSession objects.
    TInt iResourceCount;
    };




/*
CCountSubSession class

Represents a subsession of CCountSession.
*/
class CCountSubSession : public CObject
    {
public:
      // creates a new CCountSubSession object.
    static CCountSubSession* NewL(CCountSession* aSession);
    
public: 
    CCountSubSession(CCountSession* aSession); 
    void ConstructL(CCountSession* aSession);
    void SetFromStringL(const RMessage2& aMessage);
    void Increase();
    void IncreaseBy(const RMessage2& aMessage);
    void Decrease();
    void DecreaseBy(const RMessage2& aMessage);
    void Reset();
    void CounterValue(const RMessage2& aMessage);
   
protected:
      // The session that owns this CCountSubSession object.
    CCountSession* iSession;
    
private:
      // The counter value
   TInt iCount;
    };

#endif
// ComplexClientAndServer.h
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.

#if !defined(__ComplexClientAndServer_H__)
#define __ComplexClientAndServer_H__

#include <e32base.h>

//server name

_LIT(KCountServerName,"MultiCountServer");

//the server version. A version must be specifyed when creating a session with the server
const TUint KCountServMajorVersionNumber=0;
const TUint KCountServMinorVersionNumber=1;
const TUint KCountServBuildVersionNumber=1;

//
IMPORT_C TInt StartThread(RThread& aServerThread);


//opcodes used in message passing between client and server
enum TCountServRqst
    {
    ECountServCreateSubSession = 1,
    ECountServCloseSubSession,
    ECountServInitSubSession,
    ECountServCloseSession,
    ECountServIncrease,
    ECountServIncreaseBy,
    ECountServDecrease,
    ECountServDecreaseBy,
    ECountServValue,
    ECountServReset,
    ECountServResourceCountMarkStart,
    ECountServResourceCountMarkEnd,
    ECountServResourceCount
    };


enum TCountServLeave
    {
    ENonNumericString = 99
    };
    
#endif    
// ComplexServer.mmp
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.

// using relative paths for source and userinclude directories

// no special capabailties required by this DLL

TARGET        ComplexServer.dll
TARGETTYPE    dll
UID           0
VENDORID 0x70000001

SOURCEPATH    .
SOURCE        ComplexServer.cpp
SOURCE        ComplexServerCCountServer.cpp
SOURCE        ComplexServerCCountSession.cpp
SOURCE        ComplexServerCCountSubSession.cpp

USERINCLUDE   .
SYSTEMINCLUDE \Epoc32\include

LIBRARY       euser.lib

#if defined(WINS)
    deffile .\ComplexServerWINS.def
#else if defined(ARM)
    deffile .\ComplexServerARM.def
#endif
nostrictdef

CAPABILITY None

Found in: examples\Base\IPC\ClientServer\Complex

See ComplexClient: Client requiring multiple server subsessions.

[Top]


ComplexClient: client requiring multiple server subsessions

Found in: examples\Base\IPC\ClientServer\Complex

The files reproduced here are the main files contained in the examples directory. Some extra files may be needed to run the examples, and these will be found in the appropriate examples directory.


Example code

// ComplexClient.cpp
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.


#include "ComplexClientAndServer.h"
#include "ComplexClient.h"
#include "CommonFramework.h"



/**
This is the main body of the example. It makes a connection
connects with the server, causing it to be created if it does not
already exist. 

It continues by making various requests on the server, displaying
and the results of those requests.

**NOTE**: The example does not demonstrate any security features - its purpose is simply
          to demonstrate the basic principles of client/server interaction.
*/

LOCAL_C void doExampleL()
    {
    _LIT(KTxtTestingCountServer,"Testing the count server test with 2 client subsessions; these represent independent counters \n\n");
    _LIT(KTxtInitCounterAWith,"\nInitialize counter A with : ");
    _LIT(KTxtInitCounterBWith,"\nInitialize counter B with : ");
    _LIT(KTxtInitCounterFailed,"\nSetting the counter from string failed: non-numeric character detected\n");
    _LIT(KTxtInitCounterSucceeded,"\nSetting the counter from string succeededd\n");
    _LIT(KTxtGetCounterAValue,"Getting counterA value from server: %d \n");
    _LIT(KTxtGetCounterBValue,"Getting counterB value from server: %d \n"); 
    _LIT(KMsgPressAnyKey," (press any key to continue)\n");
      // Useful integer variable.
    TInt ret;
            
      // Say that we are testing the count server.
    console->Printf(KTxtTestingCountServer);
    
      // This is our handle to the server.
    RCountSession countserv;

     // Connect to the count server, starting it up if we need to 
      // (which in this example we do need to).
      // This creates a session with the server.
    User::LeaveIfError(countserv.Connect());
    

      // Set up the first subsession with the count server.
      // We need to pass our handle to the server.
    RCountSubSession counterA;
    counterA.Open(countserv);
    console->Printf(KTxtInitCounterAWith);
    
    
      // Initialise the counter by passing an illegal string - this 
      // should fail - but we will check anyway before reporting
      // a failure.
    _LIT(KTxtIllegalString,"2a");
    console->Printf(KTxtIllegalString);
    ret = counterA.SetFromString(KTxtIllegalString);
    if (ret==ENonNumericString)
        {
        console->Printf(KTxtInitCounterFailed);
        }
    else
        {
        console->Printf(KTxtInitCounterSucceeded);  
        }
    
        
      // Set up the second subsession with the count server.
      // We need to pass our handle to the server.
    RCountSubSession counterB;
    counterB.Open(countserv);
    console->Printf(KTxtInitCounterBWith);
    
    
      // Initialise the counter by passing a legal string.
    _LIT(KTxtLegalString,"100");
    console->Printf(KTxtLegalString);
    ret = counterB.SetFromString(KTxtLegalString);
    if (ret==ENonNumericString)
        {
        console->Printf(KTxtInitCounterFailed);
        }
    else
        {
        console->Printf(KTxtInitCounterSucceeded);  
        }
        
    console->Printf(KMsgPressAnyKey);
    console->Getch();
    console->ClearScreen();
    
  
      // Now get the initial values back from the server.
      // The 1st subsession should have a default value (because we initialised it with an illegal value).
      // The 2nd subsession should have the value we specified (because we initialised it witha legal value).
    console->Printf(KTxtGetCounterAValue,counterA.CounterValue());  
    console->Printf(KTxtGetCounterBValue,counterB.CounterValue()); 
    

        // Increase CounterA by the default value
    _LIT(KTxt1,"\nIncrease counterA by default value (i.e. 1)..\n");
    console->Printf(KTxt1);
    counterA.Increase();
    console->Printf(KTxtGetCounterAValue,counterA.CounterValue());  

        // Increase CounterA by 2
    _LIT(KTxt2,"\nIncrease counterA by 2..\n");
    console->Printf(KTxt2);
    counterA.IncreaseBy(2);
    console->Printf(KTxtGetCounterAValue,counterA.CounterValue());  
    
      // Increase CounterB by the default value
    _LIT(KTxt3,"\nIncrease counterB by default value (i.e. 1)..\n");
    console->Printf(KTxt3);
    counterB.Increase();
    console->Printf(KTxtGetCounterBValue,counterB.CounterValue());  
    
      // Increase CounterA by 7   
    _LIT(KTxt4,"\nIncrease counterA by 7..\n");
    console->Printf(KTxt4);
    counterA.IncreaseBy(7);
    console->Printf(KTxtGetCounterAValue,counterA.CounterValue());     
    
      // Increase CounterB by 5   
    _LIT(KTxt5,"\nIncrease counterB by 5..\n");
    console->Printf(KTxt5);
    counterB.IncreaseBy(5);
    console->Printf(KTxtGetCounterBValue,counterB.CounterValue());     
    
      // Decrease CounterA by the default value
    _LIT(KTxt6,"\nDecrease counterA..\n");
    console->Printf(KTxt6);
    counterA.Decrease();
    console->Printf(KTxtGetCounterAValue,counterA.CounterValue());     
   
      // Decrease CounterB by 3
    _LIT(KTxt7,"\nDecrease counterB by 3..\n");
    console->Printf(KTxt7);
    counterB.DecreaseBy(3);
    console->Printf(KTxtGetCounterBValue,counterB.CounterValue());     

      // Show the number of resources in use.
    _LIT(KTxt8,"\nResource count is.. %d \n");
    console->Printf(KTxt8,countserv.ResourceCount());

        //close both subsessions
        
    _LIT(KTxt9,"\nClosing counterA and then CounterB..\n");
    console->Printf(KTxt9);
    counterA.Close();
    counterB.Close();
    

      // Close the sesssion with the count server.
    countserv.Close();
    
      // NB in this example, it is possible to close the session before closing the 
      // subsessions. This is because the subsessions are themsleves closed as a
      // consequence of closing the session - also the subsessions offer a simple
      // (almost trivial) synchronous service.
      // In more complex cases, you would need to think through the consequences
      // of closing a session, while a subsession is sill open.
    }
// ComplexClientAndServer.h
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.

#if !defined(__ComplexClientAndServer_H__)
#define __ComplexClientAndServer_H__

#include <e32base.h>

//server name

_LIT(KCountServerName,"MultiCountServer");

//the server version. A version must be specifyed when creating a session with the server
const TUint KCountServMajorVersionNumber=0;
const TUint KCountServMinorVersionNumber=1;
const TUint KCountServBuildVersionNumber=1;

//
IMPORT_C TInt StartThread(RThread& aServerThread);


//opcodes used in message passing between client and server
enum TCountServRqst
    {
    ECountServCreateSubSession = 1,
    ECountServCloseSubSession,
    ECountServInitSubSession,
    ECountServCloseSession,
    ECountServIncrease,
    ECountServIncreaseBy,
    ECountServDecrease,
    ECountServDecreaseBy,
    ECountServValue,
    ECountServReset,
    ECountServResourceCountMarkStart,
    ECountServResourceCountMarkEnd,
    ECountServResourceCount
    };


enum TCountServLeave
    {
    ENonNumericString = 99
    };
    
#endif    
// ComplexClient.h
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.


#if !defined(__ComplexClient_H__)
#define __ComplexClient_H__

#include <e32base.h>

/**
RCountSession

The client-side handle to a session with the server,
The class forms a layer over the Symbian provided RSessionBase class.

The class deals with the requests made by the main example code by forwarding
them to the server. The type of request is identified by a code, one of
the TCountServRqst enum values, and arguments are passed via a TIpcArgs object.
*/
class RCountSession : public RSessionBase
    {
public:
    RCountSession();
    TInt Connect();
    TVersion Version() const;
    TInt ResourceCount();
    void Close();
private:
    RThread iServerThread; 
    };


/**
RCountSubSession

The class represents a subsession of the session represented
by RCountSession.
*/
class RCountSubSession : public RSubSessionBase
    {
public:
    TInt Open(RCountSession& aServer);
    TInt SetFromString(const TDesC& aString);
    void Close();
    void Increase();
    void Decrease();
    void IncreaseBy(TInt anInt);
    void DecreaseBy(TInt anInt);
    void Reset();
    TInt CounterValue();
    };


#endif
// ComplexClient.mmp
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.

// using relative paths for source and userinclude directories

// No special capabilities required by this executable

TARGET        ComplexClient.exe
TARGETTYPE    exe
UID           0
VENDORID 0x70000001

SOURCEPATH    .
SOURCE        ComplexClient.cpp
SOURCE        ComplexClientSession.cpp
SOURCE        ComplexClientSubSession.cpp

USERINCLUDE   .
USERINCLUDE   ..\..\..\CommonFramework
SYSTEMINCLUDE \Epoc32\include

LIBRARY       euser.lib  ComplexServer.lib

CAPABILITY None
// BLD.INF
// Component description file 
//
// Copyright (C) Symbian Software Ltd 2000-2005.  All rights reserved.

PRJ_MMPFILES

ComplexServer.mmp
ComplexClient.mmp

Description

This pair of examples shows a more complex use of the client server interface. The server can support multiple subsessions within a single session, represented by the code in ComplexServer***; ComplexClient*** is the corresponding client code.


Build Notes

The server must be built before the client. This requirement is specified in the component description file bld.inf. ComplexServer.mmp is listed before ComplexClient.mmp. Build activities relating to particular .mmp files are carried out in the order in which they are listed in the bld.inf file.


Classes used


Security issues

The example does not demonstrate security issues - its purpose is just to demonstrate the mechanics of the client server relationship.