This example demonstrates the use of transient servers that are started on demand when a client needs to use the server, and exit after the last client has disconnected, sometimes after a short delay. It demonstrates how to handle startup and shutdown situations correctly.
RSessionBase - Client-side handle to a session with a server.
CTimer - Base class for a timer active object.
CServer2 - Abstract base class for servers (version 2).
CSession2 - Represents a session (version 2) for a client thread on the server-side.
Click on the following link to download the example: transient.zip
click: browse to view the example code.
The client-server framework is an important part of the Symbian platform. There is a level of complexity in implementing a server for an application, but this example provides client interface code and server code that can be re-used in your own implementations.
Symbian platform servers are of three basic types:
Transient servers: these are started on demand when a client needs the server, and exit after the last client has disconnected - sometimes after a short delay, for example, the socket server.
System servers: these are started as part of the system boot process, for example, the kernel, the file server, the window server etc.
Local servers: these are "local" to a process to provide multi-threaded access to thread-specific resources, for example, the posix server (in the C Standard Library).
The transient server supplied here runs in a thread in its own process. This gives the server independence from its clients. This is important because if the client were to create the server as a separate thread within its own process, then the server would exit, i.e. terminate, if its owning process terminated. This would be true even if clients in other processes were still connected to it.
Equally as important:
a client does not explicitly need to start the server prior to connecting; this is done by the client interface.
the server terminates when not in use shortly after the last client disconnects.
the code handles concurrency issues, the most common of which is handling the case where the server is closing down or exiting at the same time that another client is trying to connect.
The example consists of four main parts:
Client interface: this is a DLL (t-client.dll) that is used by clients.
The client interface effectively forms the gateway to the server. All requests to the server, including requests to connect to it are routed through this.
The client interface defines just one class: RMySession, derived from RSessionBase. An RSessionBase (derived) object represents the client side session and forms a channel of communication between the client and the server. Requests to the server, and information received from the server are channelled through this object. Note that the client side session object is mirrored by a server side session object; an instance of a class derived from CSession2, which in this server is called CMySession.
The client calls Connect() to set up a connection to the server. This function attempts to start the server if necessary and then issues a request to create a session with the server. If the server fails to start, or a session cannot be created then this is reported back.Simultaneous launching of two server processes is also detected, with the second attempt failing with KErrAlreadyExists. On successful completion the connection is established, and a session is created with the server.
The client uses the Send() method to send messages to the server and the Receive() method to request information from the server. Send() is implemented by calling down to the synchronous variant of RSessionBase::SendReceive() in the base class. Synchronous means that it does not return until the server has dealt with the request.
Send() passes an operation code (or function number) identifying the request and a descriptor containing information to the server. The descriptor is wrapped into a TIpcArgs object. In practice, your client interface would probably be richer, having variants of the Send() function that take more than one parameter. You might also choose to have asynchronous variants of Send(); your client code (not the client interface) would then need to use active objects to deal with this.
Receive() is implemented by calling down to the asynchronous variant of RSessionBase::SendReceive() in the base class. Asynchronous means that the call returns immediately, but the request itself may not complete until some time later.
Receive() passes an operation code (or function number) identifying the request, a descriptor into which the server will place the information, and a TRequestStatus object, which the server will use to report how the request completes. The descriptor is wrapped into a TIpcArgs object. Again, you might want a richer client interface; perhaps different function names to represent different request types.
CancelReceive() allows the client to cancel a pending Receive() request. It is usual to provide some mechanism to cancel asynchronous requests. This is implemented by calling down into the asynchronous variant of RSessionBase::SendReceive() in the base class. Note: the Receive() method simply passes an operation code (or function number) and no data.
The operation codes (or function numbers) are defined by an enum in the header file clientserver.h. This file is included in both the client interface code and server code as both need access to these common symbols.
Server: this is an EXE (t-server.exe) that runs in its own process, and E32Main() is the entry point. After setting up heap checking (in debug builds only) the implementation creates a cleanup stack and then executes the main body of the code inside a trap harness.
The server code implements several classes:
CMyServer: this class derives from the class CServer2. The main role of this class is to handle messages sent by client threads; this includes requests from clients for connection to the server. The connection is established by starting a session with the server. The session is represented by a session object, in this case the CMySession object.
Once the CMyServer object has been created, the active scheduler can be started by the call to CActiveScheduler::Start(). This call will not return until the active scheduler is stopped by the CShutdown timer, which is primed after the last client has disconnected. In simple terms, the active scheduler is a wait loop. It sits inactive until it receives a message from (potential) clients (or until the timer completes and stops the active scheduler). Messages that represent requests for connection result in the creation of a session. Other messages are passed to the relevant CMySession active object.
CMySession: one of these objects is created for each session, i.e. as a result of a request by a client for connection to the server. Once sessions have been created, requests received by CMyServer are then forwarded to the corresponding session, and specifically to the virtual function ServiceL(), which handles Send(), Receive() and CancelReceive() requests.
CShutdown: this class represents a timer that is started before the first client has connected, and after the last client has disconnected. The timer is stopped once the server has at least one client attached to it. If the server has no clients, then there is 2 second interval before the server terminates. This is implemented as an active object.
Test code:this corresponds to the client, and uses the client interface to communicate with the server - application developers should make use of the principles applied here and include them into their own client code.
bld.inf: which is a component description file.
Related APIs
CServer2 - Abstract base class for servers (version 2).
CSession2 - Represents a session (version 2) for a client thread on the server-side.
KErrAlreadyExists - System wide error code -11 : an object already exists.
RSessionBase - Client-side handle to a session with a server.
TIpcArgs - A Version 2 client/server class that clients use to package the arguments to be sent to a server.
TRequestStatus - Indicates the completion status of a request made to a service provider.
To build the example:
You can build the example from your IDE or the command line.
If you use an IDE, import the bld.inf file of the example into your IDE, and use the build command of the IDE.
If you use the command line, open a command prompt, and set the current directory to the source code directory of the example. You can then build the example with the SBSv1 build tools with the following commands:
bldmake bldfiles
abld build
How to use bldmake and How to use abld describe how to use the SBSv1 build tools.
For the emulator, the example builds the following executables :
t-client.dll
t-server.exe
t-test.exe (test executable)
t-testc.dll (test client)
in the epoc32\release\winscw\<udeb or urel>\ folder.