Issott_Common Design Patterns for Symbian OS-The Foundations of Smartphone Software_0470516356 (779879), страница 38
Текст из файла (страница 38)
The client-side DLL should not depend on server.exeso that the server is decoupled from the client and, in particular, avoidsforcing clients to have all the static dependencies of the server. However,since the client-side DLL will be relatively small, it is normally nota problem for server.exe to depend on the client-side DLL, suchas for any types that need to be shared between the client and theserver.// Number of message slots available per session.const TInt KMyServerDefaultMessageSlots = 1;const TInt KMyServerRetryCount = 2;CLIENT–SERVER// Start the server process. Simultaneous launching of two such// processes should be detected when the second one attempts to// create the server object, failing with KErrAlreadyExistsstatic TInt StartServer(){const TUidType serverUid(KNullUid,KNullUid,KServerUid3);RProcess server;TInt r=server.Create(KMyServerImg,KNullDesC,serverUid);if (r != KErrNone){return r;}TRequestStatus stat;server.Rendezvous(stat);if (stat != KRequestPending){server.Kill(0); // Abort startup}else{server.Resume(); // Logon OK - start the server}User::WaitForRequest(stat); // Wait for start or death// We can’t use the ’exit reason’ if the server panicked as this// is the panic ’reason’ and may be ’0’ which cannot be// distinguished from KErrNoner = (server.ExitType() == EExitPanic) ? KErrGeneral : stat.Int();server.Close();return r;}// Connect to the server, attempting to start it if necessaryEXPORT_C TInt RMyClientSession::Connect(){TInt retry = KMyServerRetryCount;for (;;){TInt r = CreateSession(KMyServerName,TVersion(0,0,0),KMyServerDefaultMessageSlots);if (r != KErrNotFound && rKServer != KErrServerTerminated){return r;}if (--retry == 0){return r;}r=StartServer();if (r != KErrNone && r != KErrAlreadyExists){return r;}}}EXPORT_C TInt RMyClientSession::GetValueL()199200PROVIDING SERVICES{TPckgBuf<TInt> pckg;TIpcArgs args(&pckg);User::LeaveIfError(SendReceive(EGetValue, args)); // Synchronousreturn pckg();}EXPORT_C void RMyClientSession::Observe(TRequestStatus& aStatus){// Note that the asynchronous version of SendReceive doesn’t return// any errors because they all go through aStatusSendReceive(EObserve, aStatus);}EXPORT_C void RMyClientSession::DoActivity(TRequestStatus& aStatus){SendReceive(EDoActivityL, aStatus);}EXPORT_C void RMyClientSession::CancelActivity(){SendReceive(ECancelActivity);}Using the ServerTo illustrate how a client might work, we use a single class CClient tomanage two active objects, CObserver and CActivity, that use thetwo asynchronous actions provided by the server:void CClient::UseServerServicesL(){// Here we make a simple synchronous requestiServerValue = iMyClientSession.GetValueL();// We now call the server to perform some long-running activity// that continously changes its value// We want to observe the server for changes to its value so we create// an active object to listen for the change noticeiObserver = CObserver::NewL(iMyClientSession);// First we create an active object that will listen for the// completion notice.
We pass in a reference to the observer to tell// it when to stopiActivity = CActivity::NewL(iMyClientSession, &iObserver);// Now we make the actual service requestsiActivity->DoActivity();iObserver->Observe();}void CActivity::DoActivity(){CLIENT–SERVER201iMyClientSession.DoActivity(iStatus);SetActive();}void CObserver::Observe(){iMyClientSession.Observe(iStatus);SetActive();}The client then just has to implement CActivity::RunL() andCObserver::RunL() to handle the completion of the requests.
Forinstance:void CActivity::RunL(){iObserver.Cancel();}void CObserver::RunL(){User::LeaveIfError(iStatus);// Make the observation request againObserve();iServerValue = iMyClientSession.GetValueL();... // Do something with the value}ConsequencesPositives• Resource sharing – resources will always remain scarce on a mobiledevice so the need to share will always be there. This pattern helpsdevelopers to meet the need wherever it arises.• Protected resource – all the data is stored with the server, whichgenerally has far greater security controls than most clients. A serverin its own process also means its data is protected from misbehavingclients. Servers can better control access to resources to guaranteethat only those clients with the appropriate permissions may use theresource. Centralized data also means that updates to the data are fareasier to administer than would be possible if it were distributed.• Support for clients with diverse abilities and behavior – the physicalseparation of the server from its clients means it is able to work withmultiple clients of different abilities in the operating system.
It is alsoeasier to design how such collaboration will work.202PROVIDING SERVICES• Serialized access – this pattern removes the burden of synchronizingaccess to a shared resource from the developers of each individualclient.• Decoupled services – the mature client–server architecture implementation of Symbian OS provides a usable API and infrastructure forrealizing other smaller patterns such as Active Objects (see page 133)and Cradle (see page 273).Negatives• Latency – the physical separation of client and server means communication always requires a context switch, taking in the order ofmicroseconds,18 that adds to the latency of the service and reducesperformance.• Congestion – as the number of clients increases so does the load onthe server and the risk to quality of service guarantees. For instance,the server may no longer be able to meet performance targets forcritical use cases because it is busy with a previous less critical usecase.
It may also become bogged down with notifying other clients ofchanges that one client makes.• Reduced system robustness – the failure of a server may mean thefailure of all its dependent clients which introduces a single point offailure into the system.• Complexity – the separation between clients and servers introducescomplex indirection and concurrency situations that have to be fullyappreciated by developers. This makes testing and debugging moredifficult than with a passive relationship.
Black-box testing of thefunctionality of a server through a single client using the server’sclient session may be fairly straightforward. At the system level,however, it is difficult to sufficiently test all the scenarios that multipleclient engagement with the server entails. Developers of a serveralso face a real challenge of designing and implementing a serverwhose elements can be sufficiently unit-tested in a directly coupledconfiguration. Finally, debugging is more complex. Tracing througha typical collaboration between a client and a server can involve somuch context switching as to make tracking progress difficult.• Increased resource usage – By default the extra process required bythis pattern will consume a minimum of 21–34 KB18 of RAM; codesize is increased as a result of the complexity of the solution, and the18See Appendix A for more details.CLIENT–SERVER203multiple objects required in the client, the server and the kernel19 allconsume RAM of their own.See the variants and extensions of this pattern for other architecturalquality consequences to the use of this pattern.Example ResolvedThe example problem we chose is the File Server.
It is implemented intwo main executables:• efsrv.dll provides the client session, RFs, as well as a number ofsupporting types such as TVolumeInfo, CDir, etc. Since a clientmight wish to use multiple files at once, the sub-session20 class,RFile, is provided to support this efficiently.• efile.exe provides the executable code used to create the processin which the File Server runs. efile.exe depends on efsrv.dllto provide the common types used by both the server and its clients.To avoid duplicating a lot of the information shown above, we just givesome examples here of how the client session services are implemented.The following code shows an example where some validation of theclient’s arguments is done client-side to avoid a wasted round-trip to theserver:EXPORT_C TInt RFs::Rename(const TDesC& anOldName, const TDesC& aNewName){if (anOldName.Length() <= 0 || aNewName.Length() <= 0){return (KErrBadName);}return(SendReceive(EFsRename, TIpcArgs(&anOldName, &aNewName)));}This next example shows parameters being used to describe therequest (aDrive is the drive to get information about) as well as to returninformation (anInfo is a complex type containing drive information foraDrive):EXPORT_C TInt RFs::Drive(TDriveInfo& anInfo, TInt aDrive) const{TPckg<TDriveInfo> m(anInfo);return(SendReceive(EFsDrive, TIpcArgs(&m, aDrive)));}19 The kernel maintains a number of objects to support the client–server framework thatmirror those used on the user side such as CServer2, CSession2, etc.20See the Variants and Extensions discussion.204PROVIDING SERVICESSee the Symbian Developer Library for example code on how to usethe File Server interface.Other Known UsesThe client–server framework is one of the defining structures of SymbianOS, alongside descriptors and active objects.
It provides a concreteimplementation of a secure service and hence is at the heart of theoperating system’s security strategy. As such, it is not surprising that alarge number of technologies in the operating system have been designedas servers. Most of the base and middleware services in the operatingsystem, such as Contacts Management, Calendaring, the Graphical UserInterface Engine, the Database Management System, Telephony andSockets Communication, are all implemented in servers. Applicationclients interact with these servers through Proxies, some trivial and somevery substantive.Specific examples from the Personal Information Management technology area alone are alarmserver.exe, agnserver.exe (Calendaring), contacts.exe, and tzserver.exe.Variants and ExtensionsHere we look specifically at advanced strategies that can be applied toenhance a number of architectural qualities of a client–server implementation.