Part09 ClientServer
Part09 ClientServer
Client-Server Framework
Part One
This work is licensed under the Creative Commons Attribution-Share Alike 2.0 UK: England & Wales License.
To view a copy of this license, visit http://creativecommons.org/licenses/bysa/2.0/uk/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California,
94105, USA.
Client Server
Client-Server Framework
Note
• The client–server framework can also be used to wrap code which
contains writable static data
client server
Calling Client
#1
Client-Side
Server A Resource
Calling Client API
#2
Calling Client
#3
On Symbian OS
• The server runs in its own process
• It has a separate isolated address space
• The only access a client has to the services in question is through a
well-defined interface
For example
• The window server (for access to UI resources such as the screen and
keypad)
• The serial communications server (for access to the serial ports)
• The file system server
A Symbian OS server
• Always runs in a separate thread to its clients
• Often - but not always - runs within a separate process
Process vs Thread
Process B
Process
Process A
Msgs mediated
Msgs mediated
Client Server
Client Server
by kernel
by kernel
A client and server sharing the same A client and server in separate process
process space communicating by ITC
spaces communicating by IPC
A typical server
• Has client-side code that formats requests to pass to the
server
• Requests go via the kernel
For example
• A client of the Symbian OS file server (efile.exe)
• Is actually a client of the file server’s client-side
implementation
• Linking against the DLL which provides it (efsrv.dll)
• The client-side implementation DLL hides the details of the
private client–server communication protocol from the
calling code that uses it
Process Boundary
Client-side File
File Server
Server
Implementation
A client
• May submit a number of asynchronous requests to a server (up to
255)
• May only ever have one synchronous request outstanding at a time
RHandleBase
RSessionBase
An RSessionBase object
• Uniquely identifies a client–server session
• Is used to send messages to the server
Client-Server Classes
CServer2
CSession2
Server
RSessionBase
RMyClientAPI
For example
• Class RFs derives from RSessionBase
Class RSessionBase
• Has several overloads of CreateSession()
• To start new client–server sessions
For example
• Starting a session with the file server requires a call
to RFs::Connect()
• Which calls RSessionBase::CreateSession()
• When the session is opened successfully
corresponding kernel and server-side objects are
created
Several overloads of
RSessionBase::CreateSession()
• Take an integer parameter aAsyncMessageSlots
• This value reserves a number of slots
Such as
• The secure ID of the server process
• Or the capabilities of the calling process
Session Sharing
CSession2
RSessionBase::CreateSession()
For same process sharing RSessionBase::ShareAuto()
For different process sharing RSessionBase::ShareProtected()
Server
Thread #1
Thread #2
(may be in the same process or in
a different process to Thread #1)
RSessionBase
Client 1 Client 2
Session Sharing
Session Sharing
Alternatively
• A sharable session can be created by calling the overload
of RSessionBase::CreateSession()
For example
TInt SendReceive(TInt aFunction, const TIpcArgs &aArgs);
A TIpcArgs object
• Constitutes the “payload” for a client–server request
• It can package up to four arguments together
• And contains information about each argument’s type
Internally
• The arguments are stored in a simple array of four 32-bit values
• Consecutive arguments in the constructor’s parameter list are put
into consecutive slots in the array
Which may be
• a TInt
• an RHandleBase
• a TAny*
• a TDes16*
• a TDes8*
Client #1 CServer2
MySet(TInt aVal)
RMessagePtr2
RMessage2
For example
• Calling RSessionBase::SendReceive()
• Connecting to the server or closing the session
In a similar manner
• Ptr0() returns the contents of the first element in the request array as a
TAny* pointer
• Ptr1() the contents of the second element
• So on to the fourth element, returned by Ptr3()
This method
• Wraps a call to RThread::RequestComplete() on the client’s
thread handle
CBase
CSession2
CActive
CServer2
CServer2::StartL()
• Adds the server to the active scheduler
• Initiates the first message receive request
In particular
• Requests within the server should be handled quickly and
efficiently
For example
• A long-running asynchronous request should be delegated to
another active object running within the server
- So it can be processed as a series of incremental tasks
CServer2
CPolicyServer
On receipt of a message
• The message opcode number is used to retrieve the associated
policy index
TSpecialCase::EAlwaysPass
• The message is processed as normal by passing it to the
ServiceL() method of a session
• In the case of a connection message - creating a new session
TSpecialCase::ENotSupported
• The message is completed with KErrNotSupported
TSpecialCase::ECustomCheck
• Makes a call to the virtual function CustomSecurityCheckL()
• Returns a value to indicate whether to process the message
• Or cause it to fail
Server-Side Startup
Client–Server Framework
Client-Server Framework
Part Two
Client-Server Framework
CServer2
CSession2
Server
RSessionBase
RMyClientAPI
Client #1 CServer2
MySet(TInt aVal)
• Know the basics of how clients and servers transfer data for
synchronous and asynchronous requests
• Recognize the correct code to transfer data from a client derived
from RSessionBase to a Symbian OS server
• Know how to submit both synchronous and asynchronous client–
server requests
• Know how to convert basic and custom data types into the
appropriate payload which can be passed to the server, as both
read-only and read/write request arguments
A TIpcArgs object
• Is instantiated and passed to the server with each request
struct THydraData
{
TVersion iHydraVersion;
TInt iHeadCount;
};
class TVersion
{
public:
IMPORT_C TVersion();
IMPORT_C TVersion(TInt aMajor, TInt aMinor, TInt aBuild);
IMPORT_C TVersionName Name() const;
public:
TInt8 iMajor;
TInt8 iMinor;
TInt16 iBuild;
};
Server-side code
• Should not attempt to access a client-side object directly through a C++
pointer passed from the client to server
• The server code in this examples runs in a different process therefore
different virtual address space
• Any attempt to use a client-side pointer, server-side will result in an
access violation (and a panic)
protected:
Constructors omitted for clarity
...
private:
HBufC8* iDes1;
HBufC8* iDes2;
TInt iVal;
};
Destructor
CHerculesData::~CHerculesData()
{
delete iDes1;
delete iDes2;
}
Create a dynamic flat buffer to hold this object’s HBufC8* CHerculesData::MarshalDataL() const
member data
{
"Granularity" of dynamic buffer
const TInt KExpandSize = 128;
CBufFlat* buf = CBufFlat::NewL(KExpandSize);
CleanupStack::PushL(buf);
RBufWriteStream stream(*buf);
CleanupClosePushL(stream);
Write ’this’ to stream
ExternalizeL(stream);
CleanupStack::PopAndDestroy(&stream);
Create a heap descriptor from the buffer
HBufC8* des = HBufC8::NewL(buf->Size());
TPtr8 ptr(des->Des());
buf->Read(0, ptr, buf->Size());
Finished with buf
CleanupStack::PopAndDestroy(buf);
Return (transferring ownership to caller)
return (des);
}
Asynchronous Requests
Asynchronous Requests
Additionally
• Each session carries a run-time speed and memory overhead in
the kernel and the client- and server-side threads
To use a subsession
• A client must open a session with the server as normal
• This can then be used to create subsessions which consume fewer
resources and can be created more quickly
Performance Overhead
Performance Overhead
Performance Overhead
Performance Improvements
Performance Improvements
For example
• Components that frequently transfer data to or from the file system
generally do not use direct file system access methods such as
RFile::Read() or RFile::Write()
When storing data to file, the stream APIs buffer it on the client
side
• And pass it to the file server in one block
(Rather than passing individual chunks of data as it is received)
Performance Improvements
RWriteStream
• Uses a client-side buffer to hold the data it is passed
• Only accesses the file server to write it to file when the buffer is full
• Or if the owner of the stream calls CommitL()
RReadStream
• Pre-fills a buffer from the source file when it is created
• When the stream owner wishes to access data from the file the
stream uses this buffer to retrieve the portions of data required
(Rather than calling the file server to access the file directly)
Performance Improvements
When writing code which uses a server, such as the file server
• It is always worth considering how to make server access most efficient
Client–Server Framework