- •Contents
- •1.1 Fundamental Types
- •1.2 T Classes
- •1.3 C Classes
- •1.4 R Classes
- •1.5 M Classes
- •1.6 Static Classes
- •1.7 Buyer Beware
- •1.8 Summary
- •2 Leaves: Symbian OS Exceptions
- •2.1 Leaving Functions
- •2.3 Constructors and Destructors
- •2.4 Working with Leaving Functions
- •2.5 Trapping a Leave Using TRAP and TRAPD
- •2.6 LeaveScan
- •2.7 Summary
- •3 The Cleanup Stack
- •3.1 Using the Cleanup Stack
- •3.2 How Does the Cleanup Stack Work?
- •3.4 Using TCleanupItem for Customized Cleanup
- •3.5 Portability
- •3.6 An Incidental Note on the Use of Casts
- •3.7 Summary
- •5 Descriptors: Symbian OS Strings
- •5.3 Pointer Descriptors
- •5.4 Stack-Based Buffer Descriptors
- •5.5 Heap-Based Buffer Descriptors
- •5.6 Literal Descriptors
- •5.7 Summary
- •6 Good Descriptor Style
- •6.1 Descriptors as Parameters and Return Types
- •6.2 Common Descriptor Methods
- •6.3 The Use of HBufC Heap Descriptors
- •6.4 Externalizing and Internalizing Descriptors
- •6.5 The Overuse of TFileName
- •6.6 Useful Classes for Descriptor Manipulation
- •6.7 Summary
- •7 Dynamic Arrays and Buffers
- •7.1 CArrayX Classes
- •7.3 Why Use RArray Instead of CArrayX?
- •7.4 Dynamic Descriptor Arrays
- •7.5 Fixed-Length Arrays
- •7.6 Dynamic Buffers
- •7.7 Summary
- •8.1 Multitasking Basics
- •8.2 Event-Driven Multitasking
- •8.3 Working with Active Objects
- •8.4 Example Code
- •8.5 Threads Without an Active Scheduler
- •8.6 Application Code and Active Objects
- •8.7 Summary
- •9 Active Objects under the Hood
- •9.1 Active Object Basics
- •9.2 Responsibilities of an Active Object
- •9.3 Responsibilities of an Asynchronous Service Provider
- •9.4 Responsibilities of the Active Scheduler
- •9.5 Starting the Active Scheduler
- •9.6 Nesting the Active Scheduler
- •9.7 Extending the Active Scheduler
- •9.8 Cancellation
- •9.9 Request Completion
- •9.10 State Machines
- •9.11 Long-Running Tasks
- •9.14 Common Mistakes
- •9.15 Summary
- •10 Symbian OS Threads and Processes
- •10.2 Thread Priorities
- •10.3 Stopping a Running Thread
- •10.5 Exception Handling
- •10.6 Processes
- •10.7 Summary
- •11.2 How Do the Client and Server Fit Together?
- •11.3 How Do the Client and Server Communicate?
- •11.5 How Do Synchronous and Asynchronous Requests Differ?
- •11.6 How Is a Server Started?
- •11.7 How Many Connections Can a Client Have?
- •11.8 What Happens When a Client Disconnects?
- •11.9 What Happens If a Client Dies?
- •11.10 What Happens If a Server Dies?
- •11.15 How Many Outstanding Requests Can a Client Make to a Server?
- •11.16 Can Server Functionality Be Extended?
- •11.17 Example Code
- •11.18 Summary
- •12.2 Client Boilerplate Code
- •12.3 Starting the Server and Connecting to It from the Client
- •12.4 Server Startup Code
- •12.5 Server Classes
- •12.6 Server Shutdown
- •12.7 Accessing the Server
- •12.8 Summary
- •13 Binary Types
- •13.1 Symbian OS EXEs
- •13.2 Symbian OS DLLs
- •13.3 Writable Static Data
- •13.4 Thread-Local Storage
- •13.5 The DLL Loader
- •13.6 UIDs
- •13.8 Summary
- •14 ECOM
- •14.1 ECOM Architecture
- •14.2 Features of an ECOM Interface
- •14.3 Factory Methods
- •14.4 Implementing an ECOM Interface
- •14.5 Resource Files
- •14.6 Example Client Code
- •14.7 Summary
- •15 Panics
- •15.2 Good Panic Style
- •15.3 Symbian OS Panic Categories
- •15.4 Panicking Another Thread
- •15.5 Faults, Leaves and Panics
- •15.6 Summary
- •16 Bug Detection Using Assertions
- •16.3 Summary
- •17 Debug Macros and Test Classes
- •17.1 Heap-Checking Macros
- •17.2 Object Invariance Macros
- •17.3 Console Tests Using RTest
- •17.4 Summary
- •18 Compatibility
- •18.1 Forward and Backward Compatibility
- •18.2 Source Compatibility
- •18.3 Binary Compatibility
- •18.4 Preventing Compatibility Breaks
- •18.5 What Can I Change Without Breaking Binary Compatibility?
- •18.6 Best Practice: Planning for Future Changes
- •18.7 Compatibility and the Symbian OS Class Types
- •18.8 Summary
- •19 Thin Templates
- •20.1 Class Layout
- •20.3 Parameters and Return Values
- •20.4 Member Data and Functional Abstraction
- •20.5 Choosing Class, Method and Parameter Names
- •20.7 Summary
- •21 Good Code Style
- •21.1 Reduce the Size of Program Code
- •21.2 Use Heap Memory Carefully
- •21.3 Use Stack Memory Carefully
- •21.5 Optimize Late
- •21.6 Summary
- •Glossary
- •Bibliography and Online Resources
- •Index
SERVER SHUTDOWN |
213 |
Incidentally, a constant reference to the RMessage associated with
the request is passed into each of |
the |
request handler methods |
but |
it may, alternatively, be retrieved |
by |
the handler methods by |
call- |
ing CSharableSession::Message(). However, the asynchronous requests must store a copy of the RMessage object, because the session may be processing another, different, request message by the time the asynchronous request completes and is handled.
12.6 Server Shutdown
The timer class which manages server shutdown is shown below:
const TInt KShutdownDelay=200000; // approx 2 seconds
class CShutdown : public CTimer
{
public:
inline CShutdown(); inline void ConstructL(); inline void Start();
private:
void RunL(); };
inline CShutdown::CShutdown()
: CTimer(-1) {CActiveScheduler::Add(this);}
inline void CShutdown::ConstructL() {CTimer::ConstructL();}
inline void CShutdown::Start() {After(KShutdownDelay);}
void CShutdown::RunL()
{// Initiates server exit when the timer expires CActiveScheduler::Stop();
}
The CServer-derived object owns a CShutdown object. As I described above, the server reference-counts its connected client sessions. The shutdown timer object is started when there are no sessions connected to the server, although it is canceled if a session connects before the timer expires. When the timeout completes, the timer’s event handler calls CActiveScheduler::Stop() to terminate the server’s wait loop and destroy the server. The timeout is used to delay shutdown and prevent excessive startup/shutdown churn caused by client connections which do not quite overlap. The server’s shutdown timeout is defined by KShutdownDelay, which is set to 2 seconds.
214THE CLIENT–SERVER FRAMEWORK IN PRACTICE
12.7Accessing the Server
Finally, for reference, here is an example of how the Hercules server may be accessed and used by a client. The client-side RHerculesSession class is used to connect a session to the server and wrap the caller’s parameter data as appropriate, before passing it to the server.
void TestClientServerL()
{
__UHEAP_MARK; // Checks for memory leaks (see Chapter 17) RHerculesSession session; User::LeaveIfError(session.Connect()); CleanupClosePushL(session); // Closes the session if it leaves
_LIT8(KLionDes, "NemeanLion"); User::LeaveIfError(session.SlayNemeanLion(KLionDes, 1));
TVersion version(1,0,0); THydraData hydraData; hydraData.iHydraVersion = version; hydraData.iHeadCount = 9;
User::LeaveIfError(session.SlayHydra(hydraData));
... // Checks hydraData, which was modified by the server
TInt count;
User::LeaveIfError(session.CaptureCeryneianHind(count));
... // Checks count which was set by the server
CHerculesData* data =
CHerculesData::NewLC(_L8("test1"), _L8("test2"), 1);
User::LeaveIfError(session.SlayErymanthianBoar(*data));
TRequestStatus status; session.CleanAugeanStables(status); User::WaitForRequest(status);
// Server reads this data and updates it TBuf8<12> myBuf(_L8("testdata")); session.SlayStymphalianBirds(3, myBuf, status); User::WaitForRequest(status);
... // Inspects the contents of myBuf, modified by the server CleanupStack::PopAndDestroy(2, &session); // data, session __UHEAP_MARKEND;
}
12.8Summary
This chapter examined code for a typical client–server implementation, using a simplistic example to avoid introducing ”accidental complexity”. It is intended for those wishing to implement a server and its client-side access code, and to illustrate how the Symbian OS client–server architecture works in practice, reinforcing the theory described in Chapter 11.
SUMMARY |
215 |
The example is a transient server that runs in a separate process from its clients, with the client-side implementation in a separate DLL. The chapter discusses best practice in the following areas of code:
•the use of ”opcodes” to identify a client request
•a typical client-side RSessionBase-derived class and its ”boilerplate” code to submit requests to the server. The discussion included details of how to submit different types of parameter data to the server:
•simple built-in types
•descriptors
•flat data (such as that contained in a struct or an object of a T class)
•more complex objects, which do not have a fixed length or which contain pointers to other objects (e.g. an object of a C class).
•how to implement client-side code to start the server (which for EKA1 is different depending on whether the server is running on the Windows emulator or target hardware) and how to connect to the server
•server-side bootstrap code
•the fundamental server classes, deriving from CServer and CSharableSession, including examples of request-handling methods (for both synchronous and asynchronous requests), server-side unpacking of parameter data passed from the client, and an example of how data can be passed back to the client
•the mechanism used by a transient server to reference-count its connected client sessions and shut itself down, after a brief timeout, when all its clients have disconnected
•the implementation of a typical calling client that instantiates an object of the RSessionBase-derived client class and submits requests to the server.
This chapter also listed the twelve labors of Hercules, which the reader may, or may not, wish to commit to memory.
