source: trunk/doc/src/examples/blockingfortuneclient.qdoc

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

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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