The following analysis is based on:
Developer platform: s60 3rd edition, Feature Pack 2 SDK
Operating System: Symbian OS v9.3
I. Why use the client/server architecture?
All asynchronous services in symibian OS are implemented by the server through the C/S architecture. The client uses the specific services provided by the server.ProgramThe server receives the request messages sent to the client and processes them synchronously or asynchronously. The C/S architecture has the following advantages:
1. scalability
2. Validity: the same server can serve multiple clients.
3. Security: the server and client exist in separate processes and communicate with each other through message transmission. A client with an incorrect behavior will not cause its server to crash (however, the server can generate a serious error through the handle of the client thread ).
4. Asynchronization: the AO mechanism is used to notify the client when the server completes the request. Using ao to suspend threads rather than polling the Request status, Symbian OS reduces the processor cycle for processing the request, thus saving power, which is very important for mobile devices.
2. Process of Client/Server Architecture
The clinet and server are in different processes, and they cannot access the virtual address space of each other. Therefore, they use the message passing protocol to communicate. This communication channel is called session. A session is created by the kernel and serves as a medium for all client/server communications.
Services, especially the services provided by the system, such as file services, window services, and font and bitmap services, are all started when the system is started. Of course, if it is a self-built server, it can be started when necessary, that is, when a client sends a request. Then the server blocks at a certain point and waits for the arrival of client requests. After the client sends a request, the server sends a new sub-session to process the client's request. Then, the server continues to block the listener's request to satisfy the requests of other clients. Subsequent interaction between each client and server is completed by the session created when the server is connected.
Iii. introduction to R
The C/S architecture inevitably needs to use the r class. What is the R class? Here, "R" is the first letter of resource, which indicates the resource.
1. Introduction
A class with a prefix of "R" indicates the client handle of a resource. Applications do not actually own resources. Resources are owned by a Symbian OS server on the device, and servers manage resources. You can use these handles to access resources managed by the server and request to use its functions.
The r class does not have any common base classes, and some are segmented faction classes. For example, some classes are derived from rhandlebase-RFS, and some are not-rwindow. In general, r classes are instantiated on stacks or nested in Class C, and then opened in some way (usually opened through a method call, such as calling open () or connect ()). When using them, it is necessary to use appropriate methods to process these classes (usually using the close () function ). If this task fails, the memory of the server connected to the r class and other resources are leaked.
2. In-depth
What is the R class in Symbian? Basically, all r classes contain a handle pointing to the kernel-side object. This handle is an integer! With this integer, The dobjectix container can be indexed to the objects indicated by the R class. These objects are derived from dobject and can be referenced and counted! When the user-side R class calls the close method, the corresponding count minus 1 and reaches 0, the kernel objects will be automatically parsed.
So how does the kernel parse a 32-bit integer of handle into a pointer to the kernel object?
Struct sdobjectixrec
{
Tint16 instance;
Tint16 uniqueid;
Dobject * OBJ;
};
These pointers are packaged by sdobjectixrec and placed in the dobjectix array. Each dprocess or dthread has an array like this! (So when you create an R class, you need to specify whether it is the thread range or the process range. They will be placed in different places ).
1. According to bit30, check whether the handle exists in the thread or in the dobjectix of process.
2. Based on the handle's BIT0-14 to determine the handle is really in dobjectix, the array does not cross the border
3. Get the sdobjectixrec corresponding to this index based on the BIT0-14
4. Compare the BIT16-29 with the instance for obtaining sdobjectixrec
5. If they are consistent, these operations may require nkern: locksystem ()
6. Obtain the OBJ pointer of sdobjectixrec.
Iii. Analysis of related classes
Cserver2It is an AO that receives requests from the client thread and distributes these requests to the Client Session on the corresponding server. It can use client thread requests to create client threads on the server.
/**
Abstract base class for servers (version 2). A server must define and implement a derived class. (Note that this class shocould be used instead of cserver)
*/
Class cserver2: Public cactive
{
Public:
Import_c virtual ~ Cserver2 () = 0;
Import_c tint start (const tdesc & aname );
Import_c void startl (const tdesc & aname );
Import_c void restart ();
/**
Gets a handle to the server.
Note that the rserver2 object is classified as Symbian internal, and its member functions cannot be acessed. however, the handle can be passed to the rsessionbase: createsession () variants that take a server handle.
@ Return the handle to the server.
*/
Inline rserver2 server () const {return iServer ;}
Protected:
Inline const rmessage2 & message () const;
Import_c cserver2 (tint implements ority, tservertype Atype = eunsharablesessions );
Import_c void docancel ();
Import_c void runl ();
Import_c tint runerror (tint aerror );
PRIVATE:
/**
Creates a server-side session object.
The session represents a communication link between a client and a server, and its creation is initiated by the client through a call to one of the rsessionbase: createsession () variants.
A server must provide an implementation, which as a minimum shocould:
-Check that the version of the server is compatible with the client by comparing the client supplied version number against the server's version number; it shocould leave if there is incompatibility.
-Construct and return the server side Client Session object.
@ Param aversion the version information supplied by the client.
@ Param amessage represents the details of the client request that is requesting the creation of the session.
@ Return a pointer to the newly created server-side session object.
@ See user: queryversionsupported ()
*/
Import_c virtual csession2 * newsessionl (const tversion & aversion, const rmessage2 & amessage) const = 0;
...
...
};
Note the following two APIs:
Import_c void startl (const tdesc & aname );
In the SDK, the server with the specified name is added to the As and a request is triggered. Generally, this function is called in constructl () of the class we write. Because cserver2 is derived from cactive, we can guess that in startl, a request is triggered, and then the cactive method setactive is called to indicate that AO initiated the request.
Generally, system servers, such as file servers, fonts, bitmap servers, and window servers, are all started when the system is started. However, if we do not want this to happen on our own server, we only need to start the server when necessary, that is, when a client sends a request, in constructl () to manually call this function.
Import_c virtual csession2 * newsessionl (const tversion & aversion, const rmessage2 & amessage) const = 0;
This session represents a communication connection between a client and a server. It can be created and initialized by calling rsessionbase: createsession. That is to say, if the client calls rsessionbase: createsession (), newsessionl on the server side will be called. Assume that the server is implemented as follows: when the client calls rsessionbase: createsession (), the kernel finds the corresponding server, then, the server thread's as will check whether the client has a corresponding session on the server side. If not, newsessionl will be called to create a session. This session is used for subsequent data operations on the client and server.
When designing our own server, we need to inherit from the cserver2 class. cserver2 is an AO derived from cactive, which implements several pure virtual/virtual functions of cactive, our server class must implement the newsessionl pure virtual function. Of course, if you only implement this function, it is also useless. Generally, we will rewrite the implementation of runerror in cserver2, but will not rewrite the runl and docancel methods.
Generally, we send a request to the server so that the server will send it back after some modifications are made. Therefore, do not pass the descriptor of local variables in the sendreceive function. Generally, a data member is used.
Csession2 The Client Session of the server serves as the communication channel between the client and the server. A client thread can concurrently communicate with a server multiple threads.
/**
A session can be:
-Restricted to the creating thread
-Can be shared with other threads in the same process
-Can be shared by all threads in the system.
A server must define and implement a derived class. In particle, it must provide an implementation for the servicel () virtual function.
(Note that this class shocould be used instead of csession)
*/
Class csession2: Public cbase
{
Public:
Import_c virtual ~ Csession2 () = 0;
Public:
Inline const cserver2 * server () const;
Import_c void resourcecountmarkstart ();
Import_c void resourcecountmarkend (const rmessage2 & amessage );
Import_c virtual tint countresources ();
/**
Handles the servicing of a client request that has been passed to the server.
This function must be implemented in a derived class. The details of the request are contained within the message.
@ Param amessage the message containing the details of the client request.
*/
Virtual void servicel (const rmessage2 & amessage) = 0;
Import_c virtual void serviceerror (const rmessage2 & amessage, tint aerror );
...
...
};
Virtual void servicel (const rmessage2 & amessage) = 0;
This is a pure virtual function, so the derived class must be implemented. After the client establishes a connection with the server, it sends data requests and other operations to the server through an interface. The server session virtual function servicel () will be called, the request information of the client is included in the rmessage2 parameter. Perform corresponding processing based on amessage. function () values. After processing, call amessage. Complete (kerrnone) to inform the client that the processing is complete. Generally, the semaphore on the request thread is added to tell the client that the request is complete.
Since the server has a session, the client also has a corresponding session. The answer is yes. It is Rsessionbase It is the session handle of the client, and can communicate with the server through this client interface.
/**
Clients normally define and implement a derived class to provide a richer interface.
*/
Class rsessionbase: Public rhandlebase
{
Friend class rsubsessionbase;
...
Import_c tint open (rmessageptr2 amessage, tint aparam, townertype Atype = eownerprocess );
Import_c tint open (rmessageptr2 amessage, tint aparam, const tsecuritypolicy & aserverpolicy, townertype Atype = eownerprocess );
Import_c tint open (tint aargumentindex, townertype Atype = eownerprocess );
Import_c tint open (tint aargumentindex, const tsecuritypolicy & aserverpolicy, townertype Atype = eownerprocess );
Inline tint setreturnedhandle (tint ahandleorerror );
Import_c tint setreturnedhandle (tint ahandleorerror, const tsecuritypolicy & aserverpolicy );
Protected:
Inline tint createsession (const tdesc & aserver, const tversion & aversion );
Import_c tint createsession (const tdesc & aserver, const tversion & aversion, tint aasyncmessageslots );
Import_c tint createsession (const tdesc & aserver, const tversion & aversion, tint partition, tipcsessiontype Atype, const tsecuritypolicy * apolicy = 0, trequeststatus * AStatus = 0 );
Inline tint createsession (rserver2 aserver, const tversion & aversion );
Import_c tint createsession (rserver2 aserver, const tversion & aversion, tint aasyncmessageslots );
Import_c tint createsession (rserver2 aserver, const tversion & aversion, tint aasyncmessageslots, tipcsessiontype Atype, const tsecuritypolicy * apolicy = 0, trequeststatus * AStatus = 0 );
Inline static tint setreturnedhandle (tint ahandleorerror, rhandlebase & ahandle );
Inline tint send (tint afunction, const tipcargs & aargs) const;
Inline void sendreceive (tint afunction, const tipcargs & aargs, trequeststatus & AStatus) const;
Inline tint sendreceive (tint afunction, const tipcargs & aargs) const;
Inline tint send (tint afunction) const;
Inline void sendreceive (tint afunction, trequeststatus & AStatus) const;
Inline tint sendreceive (tint afunction) const;
...
};
This class provides many overloaded createsession () methods, which can be selected as needed. Several concepts are involved: the message slot and IPC session types, which can be used according to the SDK description, the SDK has been elaborated in detail and has not been used. I will not detail it here.
Sendreceive () also has many overload methods, mainly for the same/asynchronous choice. APIs without the trequeststatus parameter are synchronized, A client session can have only one current synchronous request to the server at the same time, and multiple asynchronous requests can.
Rmessage2 This class encapsulates the details of client requests and is passed on the client and server.
/**
An object that encapsulates the details of a client request.
*/
Class rmessage2: Public rmessageptr2
{
Friend class cserver2;
# Endif
Inline tint function () const;
Inline tint int0 () const;
Inline tint int1 () const;
Inline tint int2 () const;
Inline tint int3 () const;
Inline const Tany * ptr0 () const;
Inline const Tany * ptr1 () const;
Inline const Tany * ptr2 () const;
Inline const Tany * ptr3 () const;
Inline csession2 * Session () const;
Protected:
/**
The request type.
*/
Tint ifunction;
};
The function () corresponds to the afunction in rsessionbase: sendreceive (tint afunction, const tipcargs & aargs, trequeststatus & AStatus. The client uses the tipcargs structure to package data and send requests to the server. tipcargs supports 0 to 4 parameters, up to four, if the parameter is a simple integer value, we can get their value through the Int0-3 of rmessage2 these APIs, if it is another class new, such as a descriptor, then the Ptr0-3 () to obtain the pointer. The system encapsulates the tipcargs content into rmessage2 and parses it in servicel () of the corresponding session on the server.
4. instance profiling
The following describes the Client/Server process with the example -- clientserverasync included in the SDK.
1, in csayncdocument. in CPP's ceikappui * ccsasyncdocument: createappuil (), ccsasyncrequesthandler: newl (* Appui) is called to instantiate the handler. This handler is an AO and has a rtimeserversession isession data member, rtimeserversession inherits from the session class rsessionbase of the client. This constructl () will call isession. Connect ();
2. The connet () method of rtimeserversession will connect to the server and create a session. It first calls a static function startserver (), in which the tfindserver method is used to check whether the server is started. If not, rprocess's create () to start this new process, pass the servername. If this name is not added to .exe, the API will help you do this. If the absolute path of the executable file is not specified, the system will find it in all the \ sys \ bin directories, we will also pass in the uid3 that matches the executable file.
3. Start the server and run the tint e32main () of the server as the program entry. In threadfunctionl () of the server, a cactivesched -- active object scheduler is created first, in this example, the server -- ctimeserver inherits from cserver2, and cserver2 is an activity object. Therefore, ctimeserver is also an encapsulated activity object.
4. In ctimeserver: threadfunctionl (), ctimeserver: newlc () is called to construct the server. In the constructl () of ctimeserver, the method startl () of the base class cserver2 is called () in this method, we guess a request will be sent, waiting for the client to connect, and calling the AO base class cactive method setactive to indicate that it has triggered a request. Then, cactivescheduler: Start () is called to let the as process the request. At this time, the server has been started and waits for the client to send a request to create a session.
5. The client continues to call createsession () in rtimeserversession: connect () to create a client session to communicate with the server.
6. After receiving this request, the server's as will call the AO runl () of cserver2. In this function, it will call newsessionl (), a virtual interface that needs to be implemented by a derived class () (Here we guess the implementation of cserver2). In this function, the server will use ctimeserversession: newl () to create a session representing the client connection. Now the connection channel between the client and the server has been created for both session instances, and the client and server can communicate with each other.
The session of the client is inherited from rsessionbase and owned by a handler (which is actually an AO). The sessin of the server is inherited from csession2, which is created by the server, then it is placed in the data member of cserver2: tdblqueiter <csession2> isessioniter; while cserver2 is an AO. Can the session be simply understood as AO ??
7. The server has been started, the client has been started, and the communication lines between the client and the server have been established. In the Appui, click the menu to start the clock. asyncdocument ()-> updatetime () is called. In the document, ihandler-> requesttime (); handler (AO) is called, first, it checks whether it is still active (via the basic class method isactive () of AO). If not, it triggers a request through the Client Session, then call the method setactive () of the AO base class to activate itself.
8. The client-side session sends a request to the server by calling the sendreceive (etimeservrequesttime, argS, and AStatus) API. The args is a type parameter of tipcargs. Server session blocking waits for client requests at servicel (), and then processes the requests according to amessage. function () values. Taking case etimeservrequesttime as an example, the server first retains the message content-iMessage, then requests the server for its cheartbeat, and then calls iheartbeat-> Start (etwelveoclock, this ); to start heartbeat
9. when the request is completed, the virtual void beat () = 0 method of the Interface Class mbeating is called, that is, the method of server overload. In sendtimetosessions, the system sends a response to the first session of the isessioniter queue, through the session of the server-side session> sendtimetoclient (); method, the specific action is to call the write method of the saved message, that is, iMessage. writel (0, PTR, 0) is passed to the client and iMessage is called. complete (etimeservrequesttimecomplete); to complete the nominal service.
10. The runl () of the AO of the handle on the client side is called and processed according to the parameters in iMessage. Complete. The first is iobserver. handletimeupdate (); that is, ccsasyncappui: handletimeupdate (). This method directly calls iappview-> drawnow (); In view's draw () method, idocument is taken. time () is actually the ihandler-> time (), that is, the handle member tTime itime;
11. After a request is completed, requesttime () is called in handle. The request is sent again. A complete client/server interaction is over.
All error handling procedures ignored in the analysis here. For specific handling procedures, refer to the corresponding Code . In fact, error handling is very important.