|
1 <?xml version="1.0" encoding="utf-8"?> |
|
2 <!-- Copyright (c) 2007-2010 Nokia Corporation and/or its subsidiary(-ies) All rights reserved. --> |
|
3 <!-- This component and the accompanying materials are made available under the terms of the License |
|
4 "Eclipse Public License v1.0" which accompanies this distribution, |
|
5 and is available at the URL "http://www.eclipse.org/legal/epl-v10.html". --> |
|
6 <!-- Initial Contributors: |
|
7 Nokia Corporation - initial contribution. |
|
8 Contributors: |
|
9 --> |
|
10 <!DOCTYPE concept |
|
11 PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd"> |
|
12 <concept id="GUID-51621C76-17B5-5829-B4EC-86B453442CDC" xml:lang="en"><title>How |
|
13 to implement a simple server interface</title><shortdesc>Provides code snippets to help you to implement a simple server |
|
14 interface.</shortdesc><prolog><metadata><keywords/></metadata></prolog><conbody> |
|
15 <section id="GUID-2720017E-2EDF-4DB5-81E9-2A75E434BDE3"><title>Handling asynchronous requests</title> <p>The implementation |
|
16 of a server requires a class derived from <codeph>CServer2</codeph>. This |
|
17 is the <i>active object</i> base class responsible for handling the asynchronous |
|
18 requests from the client program. </p> <p><b>Construction and initialisation</b> </p> <p>An |
|
19 instance of the <codeph>CServer2</codeph> derived class is, typically, created |
|
20 by the server's thread function. As an active object, it needs a priority |
|
21 and this is passed as a parameter to the constructor. The choice of priority |
|
22 value depends on the server's design. If the server can, ultimately, have |
|
23 more than one active object, then it may be important for the <codeph>CServer2</codeph> active |
|
24 object to have the highest priority. </p> <p>The server can now be started. |
|
25 This is a code fragment taken from the example that can be found at <filepath>...\examples\Base\IPC\ClientServer\complex</filepath>. </p> <codeblock id="GUID-6D887396-920C-5B5B-B58E-6700371DB31C" xml:space="preserve">CCountServServer *pS=new CCountServServer(EPriorityStandard); |
|
26 __ASSERT_ALWAYS(pS!=NULL,CCountServServer::PanicServer(ESvrCreateServer)); |
|
27 ... |
|
28 |
|
29 // Start the server |
|
30 TInt err = pS->Start(KCountServerName); |
|
31 if (err != KErrNone) |
|
32 { |
|
33 CCountServServer::PanicServer(ESvrStartServer); |
|
34 }</codeblock> <p>The function <codeph>CServer2::Start()</codeph> adds |
|
35 the <codeph>CServer2</codeph> active object to the active scheduler and issues |
|
36 the first request for messages. The server is now waiting for messages. </p> <p>As |
|
37 with all active objects, the completion of requests for messages is handled |
|
38 by the <codeph>CServer2::RunL()</codeph> protected member function. </p> <p><b>Handling requests</b> </p> <p>A request for a connection by a client thread |
|
39 results in the creation of a new session. The request for a connection results |
|
40 in a call by the client/server framework to the <xref href="GUID-8E316AC4-4676-301A-9A23-659E83AA1D1C.dita#GUID-8E316AC4-4676-301A-9A23-659E83AA1D1C/GUID-3260F745-3597-349A-9754-303DC020EF18"><apiname>CServer2::NewSessionL()</apiname></xref> function. |
|
41 A derived class must provide an implementation - creating and initialising |
|
42 an instance of a <xref href="GUID-D5A30C75-E22C-34E8-913B-7D2CA6AD5C51.dita"><apiname>CSession2</apiname></xref> derived class. The framework |
|
43 takes this newly created session object to the server's queue. </p> <p>For |
|
44 a non sharable session, requests for disconnection by a client thread cause |
|
45 the relevant <codeph>CSession2</codeph> object to be deleted. The <codeph>CSession2</codeph> destructor |
|
46 should perform appropriate cleanup. </p> <p>Any other message is passed to <codeph>CSession2::ServiceL()</codeph>. |
|
47 This function must be implemented by a derived class. </p> </section> |
|
48 <section id="GUID-2E5EB078-59D7-49B3-A32A-17AF0FBD3492"><title>Server side session representation</title> <p>The base class <codeph>CSession2</codeph> represents |
|
49 a client's session on the server side. This class provides the standard session |
|
50 behaviour. A class derived from <codeph>CSession2</codeph> must be defined |
|
51 and implemented. The following class definition, taken from the example that |
|
52 can be found at: <filepath>...\examples\Base\IPC\ClientServer\simple</filepath>, |
|
53 is typical: </p> <codeblock id="GUID-8DCCB062-2630-5C2E-A590-89208928E902" xml:space="preserve">class CCountServSession : public CSession2 |
|
54 { |
|
55 public: |
|
56 CCountServSession(); |
|
57 |
|
58 //service request |
|
59 void ServiceL(const RMessage2& aMessage); |
|
60 void DispatchMessageL(const RMessage2& aMessage); |
|
61 |
|
62 // services available to initialize/increase/decrease/reset and |
|
63 // return the counter value. |
|
64 void SetFromStringL(const RMessage2& aMessage); |
|
65 void Increase(); |
|
66 void Decrease(); |
|
67 void IncreaseBy(const RMessage2& aMessage); |
|
68 void DecreaseBy(const RMessage2& aMessage); |
|
69 void CounterValue(const RMessage2& aMessage); |
|
70 void Reset(); |
|
71 |
|
72 protected: |
|
73 // panic the client |
|
74 void PanicClient(const RMessage2& aMessage,TInt aPanic) const; |
|
75 |
|
76 private: |
|
77 TInt iCount; |
|
78 };</codeblock> <p>Note the following: </p> <ul> |
|
79 <li id="GUID-D3EE4EBA-47F3-5EC4-8487-A4DBD809D2B3"><p>The function <codeph>ServiceL()</codeph> is |
|
80 called by the client/server framework to handle all messages except requests |
|
81 to connect and disconnect. </p> </li> |
|
82 <li id="GUID-C4980BE1-D852-5AAE-8782-8C98D95BA102"><p> <codeph>ServiceL()</codeph> calls <codeph>DispatchMessageL()</codeph> under |
|
83 a trap harness. </p> </li> |
|
84 <li id="GUID-19A78104-9C7C-53C7-8328-F34E2CBCF857"><p> <codeph>DispatchMessageL()</codeph> determines |
|
85 the appropriate message service function to call by examining the operation |
|
86 code of the current message. This is simply a mechanism to delegate the handling |
|
87 of different request types. </p> </li> |
|
88 <li id="GUID-9A3F4396-8561-540D-BE0B-C674A14704AE"><p>The class provides message |
|
89 service functions: <codeph>Increase()</codeph>, <codeph>IncreaseBy()</codeph> etc. |
|
90 to service specific messages from clients. </p> </li> |
|
91 <li id="GUID-023BAF14-7588-5F25-B75C-8F1126D7749F"><p>The function <codeph>SetFromStringL()</codeph> needs |
|
92 a string specified by the client and therefore needs to read data from the |
|
93 client address space. </p> </li> |
|
94 </ul> <p><b>ServiceL()</b> </p> <p>This |
|
95 is implemented as follows: </p> <codeblock id="GUID-293D2833-3C1C-5B6E-AACF-B8EC20E0F7F9" xml:space="preserve">void CCountServSession::ServiceL(const RMessage2& aMessage) |
|
96 { |
|
97 TRAPD(err,DispatchMessageL(aMessage)); |
|
98 aMessage.Complete(err); |
|
99 }</codeblock> <p>After calling the appropriate service function via <codeph>DispatchMessageL()</codeph>, |
|
100 the asynchronous request is completed with <codeph>aMessage.Complete()</codeph> which |
|
101 passes the completion code back to the client. </p> <p><b>DispatchMessageL()</b> </p> <p>This is implemented as follows: </p> <codeblock id="GUID-14C3190C-921F-5C69-B0D0-7932C71B5ACF" xml:space="preserve">void CCountServSession::DispatchMessageL(const RMessage2& aMessage) |
|
102 { |
|
103 switch (aMessage.Function()) |
|
104 { |
|
105 case ECountServSetFromString: |
|
106 SetFromStringL(aMessage); |
|
107 return; |
|
108 case ECountServIncrease: |
|
109 Increase(); |
|
110 return; |
|
111 case ECountServIncreaseBy: |
|
112 IncreaseBy(aMessage); |
|
113 return; |
|
114 ... |
|
115 case ECountServValue: |
|
116 CounterValue(aMessage); |
|
117 return; |
|
118 ... |
|
119 default: |
|
120 PanicClient(aMessage,EBadRequest); |
|
121 return; |
|
122 } |
|
123 }</codeblock> <p><b>IncreaseBy()</b> </p> <p>This message service function is implemented as follows: </p> <codeblock id="GUID-4516B089-F1D8-56BD-82CB-B7A1B0E98F0D" xml:space="preserve">void CCountServSession::IncreaseBy(const RMessage2& aMessage) |
|
124 { |
|
125 iCount = iCount + aMessage.Int0(); |
|
126 }</codeblock> <p>Note that we need to pass the message object to the function. |
|
127 The <codeph>Int0()</codeph> function is used to get the integer specified |
|
128 in the client call - the '0' on the end of the function name indicates that |
|
129 the integer is the first parameter in the set passed across from the client. </p> <p><b>SetFromStringL()</b> </p> <p>This message service function is implemented |
|
130 as follows: </p> <codeblock id="GUID-2BC55BC9-6AE1-5462-A6EE-6767F8EE19C7" xml:space="preserve">void CCountServSession::SetFromStringL(const RMessage2& aMessage) |
|
131 { |
|
132 // length of passed descriptor (1st parameter passed from client) |
|
133 TInt deslen = aMessage.GetDesLength(0); |
|
134 |
|
135 // Passed data will be saved in this descriptor. |
|
136 RBuf buffer; |
|
137 |
|
138 // Max length set to the value of "deslen", but current length is zero |
|
139 buffer.CreateL(deslen); |
|
140 |
|
141 // Do the right cleanup if anything subsequently goes wrong |
|
142 buffer.CleanupClosePushL(); |
|
143 |
|
144 // Copy the client's descriptor data into our buffer. |
|
145 aMessage.ReadL(0,buffer,0); |
|
146 |
|
147 // Now do a validation to make sure that the string only has digits |
|
148 if (buffer.Length() == 0) |
|
149 { |
|
150 User::Leave(ENonNumericString); |
|
151 } |
|
152 ... |
|
153 // Do rest of work to convert from |
|
154 // string to integer, and assign.</codeblock> <p> <codeph>RMessage::ReadL()</codeph> reads |
|
155 the descriptor from the client address space as specified by the first argument |
|
156 in the message, and copies the data into the descriptor specified as its second |
|
157 argument. A basic test is done to make sure there data is supplied. </p> <p><b>CounterValue()</b> </p> <p>This is implemented as follows: </p> <codeblock id="GUID-9EE6666F-A4A7-5437-8AF1-CB0F8E2D6F0F" xml:space="preserve">void CCountServSession::CounterValue(const RMessage2& aMessage) |
|
158 { |
|
159 TPckgBuf<TInt> p(iCount); |
|
160 aMessage.WriteL(0,p); |
|
161 }</codeblock> <p>It writes data back to a descriptor in the client address |
|
162 space. The corresponding client request is: </p> <codeblock id="GUID-A772B781-18E8-5109-BB6F-8871F829A8B5" xml:space="preserve">TInt RCountServSession::CounterValue() |
|
163 { |
|
164 TInt res=0; |
|
165 TckgBuf<TInt> pckg; |
|
166 |
|
167 // Note that TPckgBuf is of type TDes8 |
|
168 TIpcArgs args(&pckg); |
|
169 SendReceive(ECountServValue, args); |
|
170 |
|
171 // Extract the value returned from the server. |
|
172 res = pckg(); |
|
173 return res; |
|
174 }</codeblock> <p><b>Notes</b> </p> <ul> |
|
175 <li id="GUID-EC003B28-3695-5B19-8074-54DB06B720B8"><p>The <codeph>TInt</codeph> is |
|
176 packaged into a descriptor before being passed to the server. The packaging |
|
177 mechanism is known as package buffer. </p> </li> |
|
178 <li id="GUID-BB34EFC9-D238-509C-98C1-66BC64AA6EE2"><p>The write operation |
|
179 copies the descriptor, i.e. the package buffer containing the integer value, |
|
180 back to the descriptor in the client address space. Note that the zero specified |
|
181 in <codeph>aMessage.WriteL(0,p);</codeph> means that the argument referred |
|
182 to is the first in the list passed across from the client side via the <codeph>TIpcArgs</codeph> object. </p> </li> |
|
183 </ul> </section> |
|
184 </conbody></concept> |