Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 49
Текст из файла (страница 49)
Among otherthings, the DServer object hosts the queue of messages intended for itsassociated server, which retrieves them through the RServer2 proxy.class CServer2 : public CActive{public:...public:IMPORT_C virtual ∼CServer2() =0;IMPORT_C TInt Start(const TDesC& aName);IMPORT_C void StartL(const TDesC& aName);IMPORT_C void ReStart();inline RServer2 Server() const { return iServer; }protected:inline const RMessage2& Message() const;IMPORT_C CServer2(TInt aPriority,TServerType aType=EUnsharableSessions);IMPORT_C void DoCancel();IMPORT_C void RunL();IMPORT_C TInt RunError(TInt aError);IMPORT_C virtual void DoConnect(const RMessage2& aMessage);IMPORT_C virtual TInt Extension_(TUint aExtensionId, TAny*& a0,TAny* a1);private:IMPORT_C virtual CSession2* NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const = 0;void Connect(const RMessage2& aMessage);void DoConnectL(const RMessage2& aMessage,CSession2* volatile& aSession);...TInt iSessionType;RServer2 iServer;RMessage2 iMessage;TAny* iSpare;TDblQue<CSession2> iSessionQ;protected:TDblQueIter<CSession2> iSessionIter;private:void Disconnect(const RMessage2& aMessage);static void BadMessage(const RMessage2& aMessage);static void NotConnected(const RMessage2& aMessage);friend class CPolicyServer;};When developing a CServer2-derived class, you do not need todeal explicitly with the RServer2 kernel-object proxy.
The frameworkbuilds this proxy during the start-up of a CServer2, which then uses itinternally to receive messages from the DServer kernel object.When a server starts, it needs to declare a name that must be usedby all connect messages from clients. System servers use a unique namethat starts with ‘!’, so that clients (through the client API) can easily findthem and connect to them. Other servers cannot spoof the names of250INTERPROCESS COMMUNICATION MECHANISMSsystem servers because they need ProtServ platform security capabilities(see Chapter 9). Private servers should use a name that is private to theinstance of the application that started them – for example, by includingthe application’s main thread name in the server name.
This raises thequestion, how does a client work out the name of server? The answeris that the name is intimate between the client-side API and the server.Subsequently, the client-side API is implemented to use this name (i.e. byhard-coding or by inclusion of a mutual header). What is most importanthere is to make clear that you should implement the client-side of yourserver to hide this name from client-side users.Using a SessionA session is the context within which a client and a server communicate.
Clients package requests, which they then send over sessions to aserver. This is achieved on the client side by means of the Send() andSendReceive() methods of RSessionBase. For example:TInt RSomeSession::Write(TDes8& aDes, TInt aLength, TInt aMode){TIpcArgs args(&aDes, aLength, aMode);return SendReceive(ERequestWrite, args);}A client’s request contains a function number (sometimes known as arequest code) and, optionally, some associated data. The function numberis an enumeration, such as ERequestWrite, whose value and meaningis known to both the client and the server.
As we shall see later, the serveruses this value to determine the request type and what function shouldbe used to handle it.If the client has any additional data to communicate, the data needsto be packaged in a TIpcArgs structure, which may take up to fourarbitrary arguments. The TIpcArgs class has templated constructors thatalso store the type of each argument. This information is used by theSymbian OS microkernel to validate a server’s usage of the arguments inthe various Read() and Write() methods of RMessagePtr2.On the server side, the client’s request is delivered in a message tothe corresponding session’s ServiceL() method. The implementationof this method is expected to extract the request’s function number fromthe message by means of a call to RMessage2::Function().Practically, except for connection and disconnection requests, a session’s ServiceL() method acts as the dispatcher of all a client’s requeststo the parts of the server that can handle them.
A typical implementationmight look something like:void CSomeSession::ServiceL(const RMessage2& aMessage){CLIENT–SERVER IPC251...switch(aMessage.Function()){case CSomeServer::EDoSomething:{DoSomething(aMessage);aMessage.Complete(KErrNone);break;}case CSomeServer::EDoSomethingElse:{DoSomethingElseAndComplete(aMessage);break;}default:aMessage.Complete(KErrNotSupported);}...}As illustrated in this example, although ServiceL() is a potentiallyleaving function, the dispatch methods, such as DoSomething(), usually are not.
Allowing these functions to leave makes completing themessage much harder and generally needs the use of TRAP macros. Notealso, that since a server needs to complete a request via the messagehandle, dispatch methods that perform the completion must keep trackof the supplied message.Server sideCServer2CSession2Client SideRSessionBaseRSubSessionBaseFigure 8.5RSubSessionBaseClient–server class diagram252INTERPROCESS COMMUNICATION MECHANISMSIt is important to note that once a message has been completed it mayno longer be used in any way by the server.
Any such attempt results ina KERN-EXEC 44 panic (bad message handle). To avoid this situation,implementations may occasionally need to check the value returned byRMessagePtr2::IsNull(). You also need to be aware that if youmake a copy of the message, the copy does not have its state updatedby a subsequent call to Complete(). For this reason, it is best to avoidmaking copies of RMessage2 or RMessagePtr2 objects and to usereferences in method calls,If the message contains additional data, this can be read from themessage, using one or more of the methods supplied by the RMessage2class and its parent, RMessagePtr2. For example, a server could accessthe content of the aDes descriptor as follows:TInt length = aMessage.Int1(); // Get length from message param 1TPtr8 buffer = MyNewBufferL(length);// Make a new buffer for the dataaMessage.ReadL(0,buffer);// Read data from client descriptor (parameter 0)The code in this example could leave, so it might not be suitablefor inclusion in the processing performed by ServiceL() or any of itsdispatch functions, unless you protect it by means of a TRAP.Cleaning upWhen a client has finished with a client–server session, it should endthe session with a call to RSessionBase::Close().
This sends adisconnection message to the server, which responds by calling thedestructor of the CSession2-derived object to destroy its end of thesession. This action also causes the kernel to destroy its own representationof the session and sets the handle of the client-side RSessionBase classto zero. It is safe for a client to call Close() on the RSessionBaseagain; in this case Close() does nothing.If a client thread dies, the kernel performs thread-death cleanup.
Thisinvolves sending a disconnection message to all the server sessions forwhich it was a client. Thus, the server-side of the session is safelydestroyed, even if the client thread dies unexpectedly.Servers must perform effective cleanup. When a CSession2 objectis destroyed, any resources associated with it should also be destroyed.Usually, this is just standard C++ destructor processing.Servers should be written with the greatest care, so they do notterminate prematurely. However, if a server does die, then SymbianOS gives the server’s clients the opportunity to recover. All outstandingmessages from asynchronous requests are completed with the KErrDiedreturn code.PUBLISH AND SUBSCRIBE IPC253On receiving a notification that a server has died, the client shouldclean up all RSessionBase objects relating to that server.
AnySendReceive() call issued through an RSessionBase to a deadserver results in a completion code of KErrServerTerminated. Thisis the case even if a new instance of the server is started: old sessions arenot reconnected.Sessions and Sub-sessionsA new session has a handle that is passed as a parameter in any message.A single client thread may have multiple sessions to any individualserver – each session identified by a different handle. However, becausesessions (RSessionBase objects) are relatively heavyweight in terms oftheir maintenance in the kernel, there is a mechanism for multiplexingmany sub-sessions over a single session, as shown in Figure 8.5.To create a sub-session, you call RSubSessionBase::CreateSubSession(). Use of sub-sessions is private between the sessions andthe server; the kernel knows nothing about them.
Thus sub-sessions formpart of the client–server ‘protocol’ that exploits sessions. Informationabout sub-sessions is communicated to the server in the fourth IPCargument (which means that when using sub-sessions only the other threearguments are available for use by the client). An RSubSessionBaseis simply a wrapper around the RSessionBase API, using a privateRSessionBase member.It is the responsibility and design decision of the client API whethersessions or sub-sessions are to be used. Usually the decision to usesub-sessions is evident, where the client API needs the client to createa master session proxy which then is used by other proxy objects. Forexample, the file server API has RFs as its session object and RFile as afile proxy, containing a sub-session that uses the RFs session object.Before the RFile sub-session object can be used, it first needs to beinitialized with an established RFs session.