source: branches/4.5.1/doc/src/examples/blockingfortuneclient.qdoc

Last change on this file was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 10.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the documentation of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*!
43 \example network/blockingfortuneclient
44 \title Blocking Fortune Client Example
45
46 The Blocking Fortune Client example shows how to create a client for a
47 network service using QTcpSocket's synchronous API in a non-GUI thread.
48
49 \image blockingfortuneclient-example.png
50
51 QTcpSocket supports two general approaches to network programming:
52
53 \list
54
55 \o \e{The asynchronous (non-blocking) approach.} Operations are scheduled
56 and performed when control returns to Qt's event loop. When the operation
57 is finished, QTcpSocket emits a signal. For example,
58 QTcpSocket::connectToHost() returns immediately, and when the connection
59 has been established, QTcpSocket emits
60 \l{QTcpSocket::connected()}{connected()}.
61
62 \o \e{The synchronous (blocking) approach.} In non-GUI and multithreaded
63 applications, you can call the \c waitFor...() functions (e.g.,
64 QTcpSocket::waitForConnected()) to suspend the calling thread until the
65 operation has completed, instead of connecting to signals.
66
67 \endlist
68
69 The implementation is very similar to the
70 \l{network/fortuneclient}{Fortune Client} example, but instead of having
71 QTcpSocket as a member of the main class, doing asynchronous networking in
72 the main thread, we will do all network operations in a separate thread
73 and use QTcpSocket's blocking API.
74
75 The purpose of this example is to demonstrate a pattern that you can use
76 to simplify your networking code, without losing responsiveness in your
77 user interface. Use of Qt's blocking network API often leads to
78 simpler code, but because of its blocking behavior, it should only be used
79 in non-GUI threads to prevent the user interface from freezing. But
80 contrary to what many think, using threads with QThread does not
81 necessarily add unmanagable complexity to your application.
82
83 We will start with the FortuneThread class, which handles the network
84 code.
85
86 \snippet examples/network/blockingfortuneclient/fortunethread.h 0
87
88 FortuneThread is a QThread subclass that provides an API for scheduling
89 requests for fortunes, and it has signals for delivering fortunes and
90 reporting errors. You can call requestNewFortune() to request a new
91 fortune, and the result is delivered by the newFortune() signal. If any
92 error occurs, the error() signal is emitted.
93
94 It's important to notice that requestNewFortune() is called from the main,
95 GUI thread, but the host name and port values it stores will be accessed
96 from FortuneThread's thread. Because we will be reading and writing
97 FortuneThread's data members from different threads concurrently, we use
98 QMutex to synchronize access.
99
100 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 2
101
102 The requestNewFortune() function stores the host name and port of the
103 fortune server as member data, and we lock the mutex with QMutexLocker to
104 protect this data. We then start the thread, unless it is already
105 running. We will come back to the QWaitCondition::wakeOne() call later.
106
107 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 4
108 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 5
109
110 In the run() function, we start by acquiring the mutex lock, fetching the
111 host name and port from the member data, and then releasing the lock
112 again. The case that we are protecting ourselves against is that \c
113 requestNewFortune() could be called at the same time as we are fetching
114 this data. QString is \l reentrant but \e not \l{thread-safe}, and we must
115 also avoid the unlikely risk of reading the host name from one request,
116 and port of another. And as you might have guessed, FortuneThread can only
117 handle one request at a time.
118
119 The run() function now enters a loop:
120
121 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 6
122
123 The loop will continue requesting fortunes for as long as \e quit is
124 false. We start our first request by creating a QTcpSocket on the stack,
125 and then we call \l{QTcpSocket::connectToHost()}{connectToHost()}. This
126 starts an asynchronous operation which, after control returns to Qt's
127 event loop, will cause QTcpSocket to emit
128 \l{QTcpSocket::connected()}{connected()} or
129 \l{QTcpSocket::error()}{error()}.
130
131 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 8
132
133 But since we are running in a non-GUI thread, we do not have to worry
134 about blocking the user interface. So instead of entering an event loop,
135 we simply call QTcpSocket::waitForConnected(). This function will wait,
136 blocking the calling thread, until QTcpSocket emits connected() or an
137 error occurs. If connected() is emitted, the function returns true; if the
138 connection failed or timed out (which in this example happens after 5
139 seconds), false is returned. QTcpSocket::waitForConnected(), like the
140 other \c waitFor...() functions, is part of QTcpSocket's \e{blocking
141 API}.
142
143 After this statement, we have a connected socket to work with. Now it's
144 time to see what the fortune server has sent us.
145
146 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 9
147 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 10
148
149 This step is to read the size of the packet. Although we are only reading
150 two bytes here, and the \c while loop may seem to overdo it, we present this
151 code to demonstrate a good pattern for waiting for data using
152 QTcpSocket::waitForReadyRead(). It goes like this: For as long as we still
153 need more data, we call waitForReadyRead(). If it returns false,
154 we abort the operation. After this statement, we know that we have received
155 enough data.
156
157 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 11
158
159 Now we can create a QDataStream object, passing the socket to
160 QDataStream's constructor, and as in the other client examples we set
161 the stream protocol version to QDataStream::Qt_4_0, and read the size
162 of the packet.
163
164 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 12
165 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 13
166
167 Again, we'll use a loop that waits for more data by calling
168 QTcpSocket::waitForReadyRead(). In this loop, we're waiting until
169 QTcpSocket::bytesAvailable() returns the full packet size.
170
171 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 14
172
173 Now that we have all the data that we need, we can use QDataStream to
174 read the fortune string from the packet. The resulting fortune is
175 delivered by emitting newFortune().
176
177 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 15
178
179 The final part of our loop is that we acquire the mutex so that we can
180 safely read from our member data. We then let the thread go to sleep by
181 calling QWaitCondition::wait(). At this point, we can go back to
182 requestNewFortune() and look closed at the call to wakeOne():
183
184 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 1
185 \dots
186 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 3
187
188 What happened here was that because the thread falls asleep waiting for a
189 new request, we needed to wake it up again when a new request
190 arrives. QWaitCondition is often used in threads to signal a wakeup call
191 like this.
192
193 \snippet examples/network/blockingfortuneclient/fortunethread.cpp 0
194
195 Finishing off the FortuneThread walkthrough, this is the destructor that
196 sets \e quit to true, wakes up the thread and waits for the thread to exit
197 before returning. This lets the \c while loop in run() will finish its current
198 iteration. When run() returns, the thread will terminate and be destroyed.
199
200 Now for the BlockingClient class:
201
202 \snippet examples/network/blockingfortuneclient/blockingclient.h 0
203
204 BlockingClient is very similar to the Client class in the
205 \l{network/fortuneclient}{Fortune Client} example, but in this class
206 we store a FortuneThread member instead of a pointer to a QTcpSocket.
207 When the user clicks the "Get Fortune" button, the same slot is called,
208 but its implementation is slightly different:
209
210 \snippet examples/network/blockingfortuneclient/blockingclient.cpp 0
211 \snippet examples/network/blockingfortuneclient/blockingclient.cpp 1
212
213 We connect our FortuneThread's two signals newFortune() and error() (which
214 are somewhat similar to QTcpSocket::readyRead() and QTcpSocket::error() in
215 the previous example) to requestNewFortune() and displayError().
216
217 \snippet examples/network/blockingfortuneclient/blockingclient.cpp 2
218
219 The requestNewFortune() slot calls FortuneThread::requestNewFortune(),
220 which \e shedules the request. When the thread has received a new fortune
221 and emits newFortune(), our showFortune() slot is called:
222
223 \snippet examples/network/blockingfortuneclient/blockingclient.cpp 3
224 \codeline
225 \snippet examples/network/blockingfortuneclient/blockingclient.cpp 4
226
227 Here, we simply display the fortune we received as the argument.
228
229 \sa {Fortune Client Example}, {Fortune Server Example}
230*/
Note: See TracBrowser for help on using the repository browser.