Wiley.Developing.Software.for.Symbian.OS.2nd.Edition.Dec.2007 (779887), страница 61
Текст из файла (страница 61)
This will allow for alarge queue, but it sacrifices some control over memory allocation, sincekernel memory could become exhausted if the queue of messages buildsup for too long.StartServerProcess() creates and starts the process via RProcess. In StartServerProcess(), after the process is created, I callproc.Rendezvous(stat), start the process with proc.Resume(),and then wait for the Rendezvous() function to complete usingUser::WaitForRequest(stat). As described in section 9.5.6, Rendezvous() completes when the process (in this case the server process)calls RProcess::Rendezvous() or if the process terminates.
As youwill see shortly, our server process calls RProcess::Rendezvous(KErrNone) when the server is up and ready to receive commands.When the server calls the Rendezvous() function, our User::WaitForRequest(stat) will complete and StartServerProcess() will return. If StartServerProcess() returns eitherKErrNone or KErrAlreadyExists, CreateSession() is attemptedagain (which should succeed this time).
The reason the value KErrAlreadyExists is checked also is that it is possible that another clientcould be starting the server at the same time, and beats this one to theactual process creation.Invoking the server’s servicesThe following four methods of RTextBuff invoke the server’s services:•AddText() appends the specified text to the session’s text buffer.•GetText() retrieves the text from the session’s text buffer.•Reset() clears the session’s text buffer.• BackSpace() decrements the text buffer position by the indicatednumber of characters.When the application is finished with the session, it calls Close().The following shows the code for the AddText(), GetText(),BackSpace(), and Reset() functions on the client side:TInt RTextBuff::AddText(const TDesC& aText){TIpcArgs args(&aText);TInt rc = SendReceive(ETextBuffAddText,args);return rc;}void RTextBuff::GetText(TDes& aText){TIpcArgs args(&aText);SendReceive(ETextBuffGetText,args);CLIENT–SERVER EXAMPLE311}void RTextBuff::Reset(){SendReceive(ETextBuffReset);}TInt RTextBuff::BackSpace(TInt aNumChars){TIpcArgs args(aNumChars);TInt rc=SendReceive(ETextBuffBackSpace,args);return rc;}The methods here are simple wrappers that send commands (alongwith command arguments) to the server, using the SendReceive()method of RSessionBase, leaving the server to do the actual work.SendReceive() is a protected function of RSessionBase and isdefined as follows:void SendReceive(TInt aCommand, const TIpcArgs& aArgs)aCommand indicates the command that the server session is to process.You define your set of commands for the server in a header file as youwill see shortly.
The server side uses the command to determine whataction it should perform.TIpcArgs is a class that can package up to four 32-bit argumentsthat can be sent to the server along with the command. The argumentscan be in the following form:•A 32-bit integer (Backspace() uses the first argument in this way).•A pointer to a descriptor (used by AddText() and GetText() inthe example).In the example, we use TIpcArgs to package only one argument. Youcan specify more by simply adding (up to four) the arguments to the constructor (e.g., TIpcArgs args(myArg1,myArg2,myArg3,myArg4)).After sending the command, SendReceive() will wait for the serverto complete the command’s execution before returning. However, thereis also an asynchronous version of SendReceive() that is defined asfollows:void SendReceive(TInt aCommand, const TIpcArgs& aArgs, TRequestStatus&aStat)This version of SendReceive() does not block execution of the callingthread, but instead signals an asynchronous event to the calling threadwhen the server has completed execution of the command.
You can use312CLIENT–SERVER FRAMEWORKthis version to create asynchronous client functions. For example, an asynchronous version of function DoSomeFunction() can be implementedas follows:void RMyClient::DoSomeFunction(TInt aArg, TRequestStatus& aStat){TIpcArgs args(aArg);SendReceive(ESomeFunction,args,aStat);}The client program can then invoke DoSomeFunction() from an activeobject followed by a call to the active object’s SetActive(). The activeobject’s RunL() function is invoked when the server actually completesthe command.RSessionBase also provides a Send() method. Unlike SendReceive(), this function does not indicate when the server completesthe command, but simply returns once the message is sent.The commands sent to the server are defined in an include filethat needs to be included by both the server and client (I named ittextbuff.h).
It contains the server name (which here is the name ofthe process, but this doesn’t have to be the case1 ), the client–servercommands, and the version information for the server, and is defined asfollows:#include <e32base.h>_LIT(KTextBuffServerName,"TextBuffServer");//The server version.
A version must be specified when creating a sessionwith the server.const TUint KTextBuffMajorVersionNumber=0;const TUint KTextBuffMinorVersionNumber=1;const TUint KTextBuffBuildVersionNumber=1;enum TTextBuffSrvCmds{ETextBuffReset,ETextBuffAddText,ETextBuffGetText,ETextBuffBackSpace,ETextBuffCloseSession// Used later in this chapter.};10.3.2 Server ImplementationFollowing is the server’s include file (tbuffserver.h), which containsthe server and session class definitions:1Note also that system server names start with ‘!’, which are considered protectednames. A server needs the ProtServ extended capability to have a name that starts with‘!’. This protection is in place to prevent third-party servers from impersonating systemservers.CLIENT–SERVER EXAMPLE313#include "textbuff.h"enum TTextBuffPanic{EInvalidCommand,EInvalidDescriptor,EServerInitError};const TInt KMaxTextBufferSize = 4096;class CTextBuffServer : public CServer2{public:static CTextBuffServer* NewLC();virtual CSession2 *NewSessionL(const TVersion &aVersion,const RMessage2 &aMessage) const;private:CTextBuffServer();};class CTextBuffSession : public CSession2{public:static CTextBuffSession* NewL(CTextBuffServer* aServer);CTextBuffSession();private:virtual void ServiceL(const RMessage2& aMessage);virtual void ServiceError(const RMessage2& aMessage,TInt aError);CTextBuffSession() {};void ConstructL(CTextBuffServer* aServer);void DispatchMessageL(const RMessage2& aMessage);void AddTextL(TDesC& txt);void BackSpaceL(TInt aNumChars);TDesC& GetText();void Reset();void ClientPanic(const RMessage2& aMessage,TInt aPanicCode) const;RBuf iTextBuff;};First, note that I have included the common client–server headerfile textbuff.h, since the server also needs the server name, versionnumbers, and the client–server commands.The server class is derived from CServer2, and there is only oneinstance of this when the server is running.
The session class is derivedfrom CSession2, and an instance of this class is created for each sessiona client opens.The server is created and started by calling the static NewLC() functionof our CTextBuffServ class. CTextBuffServ::NewLC() and theCTextBuffServ constructor are shown below:CTextBuffServer* CTextBuffServer::NewLC(){CTextBuffServer* self=new (ELeave) CTextBuffServer;CleanupStack::PushL(self);self->StartL(KTextBuffServerName);return self;}314CLIENT–SERVER FRAMEWORKCTextBuffServer::CTextBuffServer(): CServer2(EPriorityStandard,ESharableSessions){}The constructor passes the server’s priority to the CServer2 base classas the first argument. This becomes the priority of the underlying server’sactive object.
The second argument means that the session is sharablebetween multiple threads in the same process.Note that the server priority does not necessarily define the priority ofthe server in relation to other servers, but defines its priority in relationto all active objects (which could include other servers) connected to thethread’s active scheduler.CTextBuffServ::NewLC() calls the base class method, CServer2::StartL(), to register the server and assign it a name.
StartL() addsthe server to the active scheduler and registers it with the name passed toit ( KTextBuffServerName in this case). This name is referenced froma client when the client requests a connection to the server. StartL()does not begin the server’s message processing – this occurs after thethread’s active scheduler is started.Following is the server startup code for the textbuffserv process:static void StartServerL(){// create and install an active schedulerCActiveScheduler *pA=new (ELeave) CActiveScheduler;CleanupStack::PushL(pA);CActiveScheduler::Install(pA);// create server and installCTextBuffServer *serv;serv = CTextBuffServer::NewLC();RProcess::Rendezvous(KErrNone); // ready to accept commandsCActiveScheduler::Start();CleanupStack::PopAndDestroy(2,pA);}GLDEF_C TInt E32Main(){CTrapCleanup* cleanup=CTrapCleanup::New();// create a cleanup stackTRAPD(res,StartServerL());if (res){_LIT(KTxtBuffServerPanic,"TextBuffServerExample");User::Panic(KTxtBuffServerPanic,EServerInitError);}delete cleanup;return(res);}When the server process is started, control goes to E32Main(), whichis the process entry point.
Upon entry, the program creates a cleanup stackCLIENT–SERVER EXAMPLE315and calls StartServerL(). StartServerL() creates and installsthe thread’s active scheduler and creates the server class by callingCTextBuffServ::NewLC(). Once this is done, the server is ready toaccept commands, so it calls RProcess::Rendezvous(KErrNone)to indicate this to the client (see section 10.3.1). The active scheduler isthen started with CActiveScheduler::Start().
The server is nowwaiting for a client to request a session with the server.When the server receives a command to create a session (via a clientcalling RSessionBase::CreateSession()), CServer2 invokes itsvirtual NewSessionL(). This function is implemented in the CServer2derived class and its purpose is to create, and return a pointer to, aninstance of the server’s session class.The following code shows TextBuffServ’s implementation ofNewSessionL():CSession2 *CTextBuffServer::NewSessionL(const TVersion &aVersion,const RMessage2 &aMessage /*not used here*/) const{// check version is okTVersion v(KTextBuffMajorVersionNumber,KTextBuffMinorVersionNumber,KTextBuffBuildVersionNumber);if (!User::QueryVersionSupported(v,aVersion))User::Leave(KErrNotSupported);return CTextBuffSession::NewL((CTextBuffServer*)this);}It calls CTextBuffSession::NewL() to create the CSession2-basedobject.
Thereafter all client messages, through the session that createdthis session class instance, will go to this instance.NewSessionL() also checks the version number passed to the function and, if the version required by the client is higher than that of theserver, it will leave with the error KErrNotSupported, causing theCreateSession() call on the client side to return this error.Below is the CTextBuffSession::NewL() function, along withthe secondary constructor and destructor for the class:CTextBuffSession* CTextBuffSession::NewL(CTextBuffServer* aServer){CTextBuffSession* self=new (ELeave) CTextBuffSession();CleanupStack::PushL(self);self->ConstructL(aServer);CleanupStack::Pop();return self;}// second-phase C++ constructorvoid CTextBuffSession::ConstructL(CTextBuffServer* /*aServer*/){iTextBuff.CreateL(KMaxTextBufferSize);}316CLIENT–SERVER FRAMEWORKCTextBuffSession::~CTextBuffSession(){iTextBuff.Close();}CTextBuffSession::NewL() calls the second-phase constructionmethod (ConstructL()) and uses the cleanup stack to safely handleany leave that may occur.