| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the documentation of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:FDL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Free Documentation License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Free |
| ** Documentation License version 1.3 as published by the Free Software |
| ** Foundation and appearing in the file included in the packaging of |
| ** this file. Please review the following information to ensure |
| ** the GNU Free Documentation License version 1.3 requirements |
| ** will be met: https://www.gnu.org/licenses/fdl-1.3.html. |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| /*! |
| \example blockingfortuneclient |
| \title Blocking Fortune Client Example |
| \ingroup examples-network |
| \brief Demonstrates how to create a client for a network service. |
| |
| \image blockingfortuneclient-example.png |
| |
| QTcpSocket supports two general approaches to network programming: |
| |
| \list |
| |
| \li \e{The asynchronous (non-blocking) approach.} Operations are scheduled |
| and performed when control returns to Qt's event loop. When the operation |
| is finished, QTcpSocket emits a signal. For example, |
| QTcpSocket::connectToHost() returns immediately, and when the connection |
| has been established, QTcpSocket emits |
| \l{QTcpSocket::connected()}{connected()}. |
| |
| \li \e{The synchronous (blocking) approach.} In non-GUI and multithreaded |
| applications, you can call the \c waitFor...() functions (e.g., |
| QTcpSocket::waitForConnected()) to suspend the calling thread until the |
| operation has completed, instead of connecting to signals. |
| |
| \endlist |
| |
| The implementation is very similar to the |
| \l{fortuneclient}{Fortune Client} example, but instead of having |
| QTcpSocket as a member of the main class, doing asynchronous networking in |
| the main thread, we will do all network operations in a separate thread |
| and use QTcpSocket's blocking API. |
| |
| The purpose of this example is to demonstrate a pattern that you can use |
| to simplify your networking code, without losing responsiveness in your |
| user interface. Use of Qt's blocking network API often leads to |
| simpler code, but because of its blocking behavior, it should only be used |
| in non-GUI threads to prevent the user interface from freezing. But |
| contrary to what many think, using threads with QThread does not |
| necessarily add unmanagable complexity to your application. |
| |
| We will start with the FortuneThread class, which handles the network |
| code. |
| |
| \snippet blockingfortuneclient/fortunethread.h 0 |
| |
| FortuneThread is a QThread subclass that provides an API for scheduling |
| requests for fortunes, and it has signals for delivering fortunes and |
| reporting errors. You can call requestNewFortune() to request a new |
| fortune, and the result is delivered by the newFortune() signal. If any |
| error occurs, the error() signal is emitted. |
| |
| It's important to notice that requestNewFortune() is called from the main, |
| GUI thread, but the host name and port values it stores will be accessed |
| from FortuneThread's thread. Because we will be reading and writing |
| FortuneThread's data members from different threads concurrently, we use |
| QMutex to synchronize access. |
| |
| \snippet blockingfortuneclient/fortunethread.cpp 2 |
| |
| The requestNewFortune() function stores the host name and port of the |
| fortune server as member data, and we lock the mutex with QMutexLocker to |
| protect this data. We then start the thread, unless it is already |
| running. We will come back to the QWaitCondition::wakeOne() call later. |
| |
| \snippet blockingfortuneclient/fortunethread.cpp 4 |
| \snippet blockingfortuneclient/fortunethread.cpp 5 |
| |
| In the run() function, we start by acquiring the mutex lock, fetching the |
| host name and port from the member data, and then releasing the lock |
| again. The case that we are protecting ourselves against is that \c |
| requestNewFortune() could be called at the same time as we are fetching |
| this data. QString is \l reentrant but \e not \l{thread-safe}, and we must |
| also avoid the unlikely risk of reading the host name from one request, |
| and port of another. And as you might have guessed, FortuneThread can only |
| handle one request at a time. |
| |
| The run() function now enters a loop: |
| |
| \snippet blockingfortuneclient/fortunethread.cpp 6 |
| |
| The loop will continue requesting fortunes for as long as \e quit is |
| false. We start our first request by creating a QTcpSocket on the stack, |
| and then we call \l{QTcpSocket::connectToHost()}{connectToHost()}. This |
| starts an asynchronous operation which, after control returns to Qt's |
| event loop, will cause QTcpSocket to emit |
| \l{QTcpSocket::connected()}{connected()} or |
| \l{QTcpSocket::error()}{error()}. |
| |
| \snippet blockingfortuneclient/fortunethread.cpp 8 |
| |
| But since we are running in a non-GUI thread, we do not have to worry |
| about blocking the user interface. So instead of entering an event loop, |
| we simply call QTcpSocket::waitForConnected(). This function will wait, |
| blocking the calling thread, until QTcpSocket emits connected() or an |
| error occurs. If connected() is emitted, the function returns true; if the |
| connection failed or timed out (which in this example happens after 5 |
| seconds), false is returned. QTcpSocket::waitForConnected(), like the |
| other \c waitFor...() functions, is part of QTcpSocket's \e{blocking |
| API}. |
| |
| After this statement, we have a connected socket to work with. |
| |
| \snippet blockingfortuneclient/fortunethread.cpp 11 |
| |
| Now we can create a QDataStream object, passing the socket to |
| QDataStream's constructor, and as in the other client examples we set |
| the stream protocol version to QDataStream::Qt_4_0. |
| |
| \snippet blockingfortuneclient/fortunethread.cpp 12 |
| |
| We proceed by initiating a loop that waits for the fortune string data by |
| calling QTcpSocket::waitForReadyRead(). If it returns false, we abort the |
| operation. After this statement, we start a stream read transaction. We |
| exit the loop when QDataStream::commitTransaction() returns true, which |
| means successful fortune string loading. The resulting fortune is |
| delivered by emitting newFortune(): |
| |
| \snippet blockingfortuneclient/fortunethread.cpp 15 |
| |
| The final part of our loop is that we acquire the mutex so that we can |
| safely read from our member data. We then let the thread go to sleep by |
| calling QWaitCondition::wait(). At this point, we can go back to |
| requestNewFortune() and look closed at the call to wakeOne(): |
| |
| \snippet blockingfortuneclient/fortunethread.cpp 1 |
| \dots |
| \snippet blockingfortuneclient/fortunethread.cpp 3 |
| |
| What happened here was that because the thread falls asleep waiting for a |
| new request, we needed to wake it up again when a new request |
| arrives. QWaitCondition is often used in threads to signal a wakeup call |
| like this. |
| |
| \snippet blockingfortuneclient/fortunethread.cpp 0 |
| |
| Finishing off the FortuneThread walkthrough, this is the destructor that |
| sets \e quit to true, wakes up the thread and waits for the thread to exit |
| before returning. This lets the \c while loop in run() will finish its current |
| iteration. When run() returns, the thread will terminate and be destroyed. |
| |
| Now for the BlockingClient class: |
| |
| \snippet blockingfortuneclient/blockingclient.h 0 |
| |
| BlockingClient is very similar to the Client class in the |
| \l{fortuneclient}{Fortune Client} example, but in this class |
| we store a FortuneThread member instead of a pointer to a QTcpSocket. |
| When the user clicks the "Get Fortune" button, the same slot is called, |
| but its implementation is slightly different: |
| |
| \snippet blockingfortuneclient/blockingclient.cpp 0 |
| \snippet blockingfortuneclient/blockingclient.cpp 1 |
| |
| We connect our FortuneThread's two signals newFortune() and error() (which |
| are somewhat similar to QTcpSocket::readyRead() and QTcpSocket::error() in |
| the previous example) to requestNewFortune() and displayError(). |
| |
| \snippet blockingfortuneclient/blockingclient.cpp 2 |
| |
| The requestNewFortune() slot calls FortuneThread::requestNewFortune(), |
| which \e shedules the request. When the thread has received a new fortune |
| and emits newFortune(), our showFortune() slot is called: |
| |
| \snippet blockingfortuneclient/blockingclient.cpp 3 |
| \codeline |
| \snippet blockingfortuneclient/blockingclient.cpp 4 |
| |
| Here, we simply display the fortune we received as the argument. |
| |
| \sa {Fortune Client Example}, {Fortune Server Example} |
| */ |