Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
++
++ /*!
++ \example blockingfortuneclient
++ \title Blocking Fortune Client
++ \examplecategory {Networking}
++ \meta tags {tcp,network,threading,synchronous-io}
++ \ingroup examples-network
++ \brief Demonstrates how to create a client for a network service.
++
++ \image blockingfortuneclient-example.png {The user provides server details and Q TcpSocket's blocking API is used to complete network operations}
++
++ 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_6_5.
++
++ \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 closely 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
++
++ 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 1
++
++ The requestNewFortune() slot calls FortuneThread::requestNewFortune(),
++ which \e schedules the request. When the thread has received a new fortune
++ and emits newFortune(), our showFortune() slot is called:
++
++ \snippet blockingfortuneclient/blockingclient.cpp 2
++ \codeline
++ \snippet blockingfortuneclient/blockingclient.cpp 3
++
++ Here, we simply display the fortune we received as the argument.
++
++ \sa {Fortune Client}, {Fortune Server}
++ */