Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 46
Текст из файла (страница 46)
The system creates a singleinstance of CHerculeanServer on server startup. The CServer baseclass manages a doubly-linked list of connected client sessions. Whenthe server class receives a client request, it passes it to the associatedCHerculeanSession object for handling.Let’s examine the code for the CHerculeanServer class in moredetail (I’ve omitted some of the straightforward construction code to keepthe code sample as short as possible):SERVER CLASSES207// Starts the server and constructs the shutdown object, starting the// timer to ensure that the server will exit even if the starting client// connection failsvoid CHerculeanServer::ConstructL(){StartL(KServerName);iShutdown.ConstructL();iShutdown.Start(); // In case the client session fails to connect}void CHerculeanServer::AddSession(){++iSessionCount;iShutdown.Cancel();// Cancel the shutdown timer now}// Decrement the session counter.
Start the shutdown timer when the last// client disconnectsvoid CHerculeanServer::RemoveSession(){if (--iSessionCount==0)iShutdown.Start();}TInt CHerculeanServer::RunError(TInt aError){if (KErrBadDescriptor==aError)PanicClient(Message(),EPanicBadDescriptor);elseMessage().Complete(aError);ReStart();// Continue reading client requestsreturn (KErrNone); // handled the error}The construction of the CHerculeanServer class is straightforward.The shutdown timer is started when the server is first constructed, in caseconstruction of the initial client session fails. When the first session isadded successfully, the session count is incremented and the shutdowntimer is canceled. The server object increments and decrements theiSessionCount reference count accordingly when sessions are addedand removed.CHerculeanServer::RunError() is called if a leave occurs inCHerculeanSession::ServiceL(), that is, if one of the methods which services client requests leaves.
The leave code is passedto RunError(), which should attempt to handle the leave, returningKErrNone if it does so. CServer::RunError() was added to Symbian OS v6.0 to allow the server to manage leaves resulting from clientrequest processing. Previously, the leaves were propagated to the activescheduler which did not have sufficient context in which to handle them.RunError() panics the client if the leave code is KErrBadDescriptor, because this indicates that client data has been passedto the server without having been properly ”descriptorized”.
This is208THE CLIENT–SERVER FRAMEWORK IN PRACTICEindicative of a programming error, so the server is justified in panickingthe client. Under all other circumstances, the server reports the error tothe client by completing it using RMessage::Complete(). It is rarelycorrect to panic another thread except to indicate a programming error.The code used by CHerculeanServer to panic the client is shownbelow. RMessage::Panic() uses the RThread handle it holds for theclient thread to panic it and also completes the outstanding client requestto allow the kernel to perform the necessary cleanup.enum TServerPanic{EPanicBadDescriptor,EPanicNotSupported};void PanicClient(const RMessage& aMessage,TServerPanic aPanic){_LIT(KPanic,"HerculesServer");aMessage.Panic(KPanic,aPanic);}A leave from CHerculeanSession::ServiceL() results in anearly return from CServer::RunL(), which skips the call to continuerequesting client messages.
From Chapter 11, you’ll recall that, on receiptof a client request, CServer::RunL() calls the ServiceL() methodof the associated CSharableSession-derived object. It is for this reasonthat RunError() must call CServer::Restart().Moving on, let’s consider the implementation of CHerculeanSession. This consists of an implementation of the ServiceL()method, which was declared pure virtual in the CSharableSessionbase class, and a set of private methods to handle client requests:void CHerculeanSession::CreateL(const CServer& aServer){// Called by the CServer frameworkCSharableSession::CreateL(aServer); // Cannot leaveServer().AddSession();// Create the CAsyncHandler object (iAsyncRequestHandler)...}CHerculeanSession::∼CHerculeanSession(){Server().RemoveSession();delete iAsyncRequestHandler;delete iClientBuf;}// Handle a client request// Leaves are handled by CHerculeanServer::RunError() which is called// by CServer::RunL()void CHerculeanSession::ServiceL(const RMessage& aMessage)SERVER CLASSES{switch (aMessage.Function()){case ESlayNemeanLion:SlayNemeanLionL(aMessage);break;case ESlayHydra:SlayHydraL(aMessage);break;case ECaptureCeryneianHind:CaptureCeryneianHindL(aMessage);break;case ESlayErymanthianBoar:SlayErymanthianBoarL(aMessage);break;case ECleanAugeanStables:CleanAugeanStablesL(aMessage);break;case ECancelCleanAugeanStables:CancelCleanAugeanStables();break;case ESlayStymphalianBirds:SlayStymphalianBirdsL(aMessage);break;case ECancelSlayStymphalianBirds:CancelSlayStymphalianBirds();break;case ECaptureCretanBull: // Omitted for claritycase ECaptureMaresOfDiomedes:case EObtainGirdleOfHippolyta:case ECaptureOxenOfGeryon:case ETakeGoldenApplesOfHesperides:case ECaptureCerberus:default:PanicClient(aMessage, EPanicNotSupported);break;}}// p[0] contains const TDesC8&// p[1] contains TIntvoid CHerculeanSession::SlayNemeanLionL(const RMessage& aMessage){const TInt KMaxLionDes = 100;TBuf8<KMaxLionDes> lionDes;aMessage.ReadL(aMessage.Ptr0(), lionDes);TInt val = aMessage.Int1();// ...
Process as necessaryaMessage.Complete(KErrNone);}// p[0] contains TPckg<THydraData>void CHerculeanSession::SlayHydraL(const RMessage& aMessage){THydraData hydraData;TPckg<THydraData> des(hydraData);aMessage.ReadL(aMessage.Ptr0(), des);// ... Process as necessary, updates hydraData.iHeadCount// Write hydraData update back to clientaMessage.WriteL(aMessage.Ptr0(), des);aMessage.Complete(KErrNone);}// p[0] contains TInt&void CHerculeanSession::CaptureCeryneianHindL(const RMessage& aMessage){209210THE CLIENT–SERVER FRAMEWORK IN PRACTICETInt count;// ... Process as necessary (updates count)TPckgC<TInt> countDes(count);aMessage.WriteL(aMessage.Ptr0(), countDes);aMessage.Complete(KErrNone);}// p[0] contains streamed CHerculesDatavoid CHerculeanSession::SlayErymanthianBoarL(const RMessage& aMessage){HBufC8* desData = HBufC8::NewLC(KMaxCHerculesDataLength);TPtr8 readPtr(desData->Des());aMessage.ReadL(aMessage.Ptr0(), readPtr);CHerculesData* data = CHerculesData::NewLC(*desData);// ...
Process as appropriate, passing in dataaMessage.Complete(KErrNone);}// Asynchronous method - no client parametersvoid CHerculeanSession::CleanAugeanStablesL(const RMessage& aMessage){// Makes an asynchronous request via the CAsyncHandler active object// (initialized with aMessage to allow it to complete the client)...}void CHerculeanSession::CancelCleanAugeanStablesL(){... // Calls Cancel() on the CAsyncHandler active object which// checks if a request is outstanding and cancels it}// Asynchronous method// p[0] contains TInt// p[1] contains TDes8&void CHerculeanSession::SlayStymphalianBirdsL(const RMessage& aMessage){TInt val0 = aMessage.Int0();// Determine the length of the client descriptor passed to the serverTInt clientDesMaxLen =aMessage.Client().GetDesMaxLength(aMessage.Ptr1());if (iClientBuf){delete iClientBuf;iClientBuf = NULL;}// iClientBuf owned/destroyed by sessioniClientBuf = HBufC8::NewL(clientDesMaxLen);TPtr8 ptr(iClientBuf->Des());aMessage.ReadL(aMessage.Ptr1(), ptr);// Makes an asynchronous request via the CAsyncHandler active object// which is initialized with aMessage to allow it to complete the// client.
Modifies the contents of iClientBuf and writes it back to// the client}void CHerculeanSession::CancelSlayStymphalianBirdsL(){SERVER CLASSES211... // Calls Cancel() on the CAsyncHandler active object which// checks if a request is outstanding and cancels it}ServiceL() consists of a switch statement that examines the clientrequest opcode, using RMessage::Function(), and calls the associated handler method for that request. In the example code above, I’ve onlyshown some of the request handling methods that CHerculesSessionimplements.You’ll recall that the client-side request code consisted of boilerplate”packaging” of parameter data to pass to the server.
By extension,the server-side request code unpacks the parameter data, performs thenecessary processing upon it, repackages return data if necessary andnotifies the client that the request has been completed. Let’s now examineeach of those stages in turn.The parameter unpacking code is fairly routine, as you can see from theimplementation of CHerculeanSession::SlayNemeanLionL().The client writes a pointer to a constant TDesC8 into the first elementof the request data array. The server retrieves this data by instantiatinga modifiable descriptor and using RMessage::ReadL()6 to read datafrom the client thread into it.
The TAny pointer to the location of the clientdescriptor is identified in this case by use of RMessage::Ptr0() – ifthe descriptor had been in the second slot in the request array, RMessage::Ptr1() would have been used, and so on.In this example, the predetermined protocol between client and serverhas fixed the maximum size of the client-side descriptor as KMaxLionDesbytes, so the server allocates a stack-based TBuf8 with that maximumsize to receive the incoming data. However, if the size of the data isunknown at compile time, as in SlayStymphalianBirdsL(), theserver must determine the size of the incoming descriptor to ensure thata sufficiently large descriptor is allocated on the server side to receive theclient data.
It can do this by calling RThread::GetDesMaxLength()on the client thread, passing in the pointer to the descriptor. It also needsto perform this check before writing descriptor data back to the client, todetermine whether the client has allocated a large enough descriptor.The use of a heap-based descriptor to read data from the client ismore appropriate if a large or unpredictable amount of data is transferred between the client and server, because the amount of stack spaceavailable is restricted on Symbian OS.SlayNemeanLionL() retrieves a TInt from the second element ofthe request data array, using RMessage::Int1(), which returns theclient parameter in the second ”slot” as an integer value.
Don’t let thezero-based numbering scheme confuse matters here!6RMessage::ReadL() performsinter-threadRThread::ReadL(), as discussed in Chapter 10.datatransferbycalling212THE CLIENT–SERVER FRAMEWORK IN PRACTICEHaving retrieved the client parameters, the request-handling function then performs any necessary processing upon it – I’ve omittedthis from the example code to keep it straightforward.
SlayNemeanLionL() is a simple example because it is synchronous and doesn’tpackage any return data to send to the client. Thus, when the requestprocessing is finished, the server simply notifies the client by callingRMessage::Complete(), which signals the client thread’s requestsemaphore to indicate request completion.CaptureCeryneianHindL() shows the server writing data back tothe client thread – in this case, it updates the integer value passed intothe first element of the request data array.