Wiley.Developing.Software.for.Symbian.OS.2nd.Edition.Dec.2007 (779887), страница 60
Текст из файла (страница 60)
On the serverside, a session is represented by an instance of a session class (derivedfrom CSession2), and this allows context, such as state information, tobe saved for that client while commands are being processed.Note that some of the server-side classes end in a ‘2’. This is to indicatethat it is version two of the client–server framework. Version 2 of thisA LOOK AT THE CLIENT–SERVER CLASSES305framework was introduced in Symbian OS v8 and is therefore in SymbianOS v9 also. It replaced the older framework used in previous OS versions,and was introduced to make client–server communication more secure.This chapter covers version 2 of the client framework only and thus onlyapplies to Symbian OS v8 and v9 (although the earlier framework is notthat much different).10.2 A Look at the Client–Server ClassesFirst, let’s look at the key framework classes you need to use for developingboth a server and its corresponding client-side implementation class.Figure 10.1 shows the basic classes used in the client–serverframework.CActiveRSessionBaseCServer2ClientInterfaceclassServer classCSession2Session classRMessage2Client SideServer SideFigure 10.1 Key Client–Server ClassesOn the client side, RSessionBase is the class from which you deriveyour server’s client-side class.
It represents a session with the server.RSessionBase methods exist to establish a session with the server andsend commands to it. Key methods of RSessionBase are:• CreateSession()Your derived class calls this protected function to create a sessionwith the server.306CLIENT–SERVER FRAMEWORK•SendReceive() and Send()Use these methods in your derived class to send messages to theserver through the created session.On the server side, you need to create classes derived from CServer2.This is the main server class that initially receives all server messages,establishes new sessions, and routes received commands to the appropriate session. CServer2 is derived from CActive.
There is just oneinstance of your CServer2-derived class for each server. The key methods of CServer2 are:•NewSessionL()Your CServer2-derived class should implement this virtual functionto create a new session (i.e., to instantiate and return an instanceof your CSession2-derived class). NewSessionL() is called whena client requests a session with the server (via the client methodCreateSession()).•StartL()This CServer2 method should be called when creating the server(usually from within a static NewL() function in a derived class).StartL() registers your server with the active scheduler and assignsit a name.The class CSession2 is the base class for a session object.
An instanceof a CSession2-derived class is created for each client session (byCServer2’s NewSessionL()). The session class handles the commandssent from the client. You override CSession2’s virtual ServiceL()method to handle the commands.RMessage2 is a server-side class that represents the message fromthe client, with methods to access it. It is a concrete class, and youdo not derive from it yourself, but use it directly. The message consists of a command code and four 32-bit arguments.
The messages arepassed into the server by the kernel, which creates them from the commands sent from the client through RSessionBase::SendReceive()or RSessionBase::Send(). Although not shown in Figure 10.1,RMessage2 is derived from a class called RMessagePtr2, whichhandled the details of the communication between the client and theserver.10.3Client–Server ExampleThe simplest way to explain the client–server framework is by walkingthrough an example. This section steps through a basic client–serverexample called TextBuffServ.TextBuffServ maintains a text buffer for each client session andallows the client to append text to the session buffer, retrieve the textCLIENT–SERVER EXAMPLE307buffer, clear the buffer, and backspace from its current position.
In thiscase, the server is created as an independent process.The example consists of the following components:•A client-side interface class.•A process TextBuffServ.exe that contains the server.Note that the purpose of this example is purely to illustrate client–serverconcepts – clearly, there are better ways to implement the example’sfunctionality without using a server (such as the direct use of descriptorswithin your application’s code). There are also performance issues toconsider, since a context switch can occur for each server command.When servers reside in their own processes, as they typically do, thiscan be even more expensive since a process switch is relatively timeconsuming, as discussed in section 3.5.7.
If you do decide to use aclient–server approach, it’s a good idea to implement fewer servercommands passing more data rather than the reverse, in order to minimizecontext switches.10.3.1Client-Side ClassFirst let’s look at the client-side class declaration (I put this in an includefile named textbuffclient.h, which all client applications wouldinclude):class RTextBuff : public RSessionBase{public:RTextBuff() { };TInt Connect();TInt AddText(const TDesC& aText);void GetText(TDes& aText);void Reset();TInt BackSpace(TInt aNumChars);TVersion Version() const;private:TInt StartServerProcess();};The client class is derived from RSessionBase, which inherits fromRHandleBase.Connecting to the serverThe Connect() method starts the server, if it’s not already started, andcreates a session with the server.
Applications always call this method first.When the application is finished with the session, it calls the Close()method, which RTextBuff inherits from RHandleBase.308CLIENT–SERVER FRAMEWORKThe following shows the Connect() method for our example:_LIT(KTextBuffServerProcess,"textbuffserver.exe");TInt RTextBuff::Connect(){TInt res;res=CreateSession(KTextBuffServerName,Version());if (res == KErrNotFound || res == KErrServerTerminated){res = StartServerProcess();if (res == KErrNone || res == KErrAlreadyExists)res=CreateSession(KTextBuffServerName,Version());}return(res);}TInt RTextBuff::StartServerProcess(){RProcess proc;TInt res = proc.Create(KTextBuffServerProcess,KNullDesC);if (res == KErrNone){TRequestStatus stat;proc.Rendezvous(stat);proc.Resume(); // start the process runningUser::WaitForRequest(stat); // wait for server to be// ready for commandsres = stat.Int();proc.Close();// finished with the handle}return(res);}In Connect() we create our client session by calling RSessionBase::CreateSession():res=CreateSession(KTextBuffServerName,Version());where the first argument is a name (defined in our client header file)that we have assigned to the server, and the second argument is thereturn value of Version().
Version() returns a TVersion objectthat contains a major version number, a minor version number, and abuild version number that are defined in our header.Our implementation of Version() for the example is as follows:TVersion RTextBuff::Version() const{return(TVersion(KTextBuffMajorVersionNumber,KTextBuffMinorVersionNumber, KTextBuffBuildVersionNumber));}CLIENT–SERVER EXAMPLE309And the version definitions in our header file are as follows:const TUint KTextBuffMajorVersionNumber=0;const TUint KTextBuffMinorVersionNumber=1;const TUint KTextBuffBuildVersionNumber=1;(The complete header is shown later in this section.)What is the version argument used for when creating a session? Inbrief, it indicates the earliest version of the server that the client will workwith. Hence, if you change your client code so that it will no longerwork with older versions of the server, then you would increase theversion number – for example, by incrementing the minor number – inthe header file.
Then, if this new version of the client is used with anolder, incompatible version of the server, the version number that theclient passes in the CreateSession() call is higher than the versionnumber built into the server, causing the server to return an error.As you will see in the server-side example code, the version numberfrom CreateSession() is passed to the NewSessionL() functionof your CServer2 class, which is where the actual version comparisonis made, using a call to User::QueryVersionSupported(). If thecomparison fails, NewSessionL() leaves with the error KErrNotSupported (causing CreateSession() – and thus the client Connect()method – to return KErrNotSupported).If CreateSession() returns KErrNotFound, then the system couldnot find a server with the specified server name, indicating that our serverhad not been started yet, and that we need to start it.
Likewise, theserver is started if CreateSession() returns KErrServerTerminated, which indicates, as the constant implies, that the server hadterminated unexpectedly. We start the server by calling StartServerProcess(), which uses RProcess to launch the server process. Wethen call CreateSession() again. CreateSession(), and henceConnect(), will either return KErrNone once the server is up andrunning, or it will return an error.Another version of CreateSession() exists that has the form:TInt CreateSession(TDesC& aName,TVersion aVer, TInt aNumSlots)aNumSlots determines how many messages can be queued from theclient to the server at one time (a space for one message being a slot).The advantage of using this version is that the slots are preallocated whenyou create the session and thus memory use is more controlled.If this version of CreateSession() is used without this last aNumSlots argument (as in the example), then memory on the kernel heap310CLIENT–SERVER FRAMEWORKis allocated for each message as it is queued up.