doc/src/examples/blockingfortuneclient.qdoc
branchRCL_3
changeset 7 3f74d0d4af4c
equal deleted inserted replaced
6:dee5afe5301f 7:3f74d0d4af4c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    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 */