1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** GNU Free Documentation License
** 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.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms
** and conditions contained in a signed written agreement between you
** and Nokia.
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example network/fortuneclient
\title Fortune Client Example
The Fortune Client example shows how to create a client for a simple
network service using QTcpSocket. It is intended to be run alongside the
\l{network/fortuneserver}{Fortune Server} example or
the \l{network/threadedfortuneserver}{Threaded Fortune Server} example.
\image fortuneclient-example.png Screenshot of the Fortune Client example
This example uses a simple QDataStream-based data transfer protocol to
request a line of text from a fortune server (from the
\l{network/fortuneserver}{Fortune Server} example). The client requests a
fortune by simply connecting to the server. The server then responds with
a 16-bit (quint16) integer containing the length of the fortune text,
followed by a QString.
QTcpSocket supports two general approaches to network programming:
\list
\o \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()}.
\o \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
In this example, we will demonstrate the asynchronous approach. The
\l{network/blockingfortuneclient}{Blocking Fortune Client} example
illustrates the synchronous approach.
Our class contains some data and a few private slots:
\snippet examples/network/fortuneclient/client.h 0
Other than the widgets that make up the GUI, the data members include a
QTcpSocket pointer, a copy of the fortune text currently displayed, and
the size of the packet we are currently reading (more on this later).
The socket is initialized in the Client constructor. We'll pass the main
widget as parent, so that we won't have to worry about deleting the
socket:
\snippet examples/network/fortuneclient/client.cpp 0
\dots
\snippet examples/network/fortuneclient/client.cpp 1
The only QTcpSocket signals we need in this example are
QTcpSocket::readyRead(), signifying that data has been received, and
QTcpSocket::error(), which we will use to catch any connection errors:
\dots
\snippet examples/network/fortuneclient/client.cpp 3
\dots
\snippet examples/network/fortuneclient/client.cpp 5
Clicking the \gui{Get Fortune} button will invoke the \c
requestNewFortune() slot:
\snippet examples/network/fortuneclient/client.cpp 6
In this slot, we initialize \c blockSize to 0, preparing to read a new block
of data. Because we allow the user to click \gui{Get Fortune} before the
previous connection finished closing, we start off by aborting the
previous connection by calling QTcpSocket::abort(). (On an unconnected
socket, this function does nothing.) We then proceed to connecting to the
fortune server by calling QTcpSocket::connectToHost(), passing the
hostname and port from the user interface as arguments.
As a result of calling \l{QTcpSocket::connectToHost()}{connectToHost()},
one of two things can happen:
\list
\o \e{The connection is established.} In this case, the server will send us a
fortune. QTcpSocket will emit \l{QTcpSocket::readyRead()}{readyRead()}
every time it receives a block of data.
\o \e{An error occurs.} We need to inform the user if the connection
failed or was broken. In this case, QTcpSocket will emit
\l{QTcpSocket::error()}{error()}, and \c Client::displayError() will be
called.
\endlist
Let's go through the \l{QTcpSocket::error()}{error()} case first:
\snippet examples/network/fortuneclient/client.cpp 13
We pop up all errors in a dialog using
QMessageBox::information(). QTcpSocket::RemoteHostClosedError is silently
ignored, because the fortune server protocol ends with the server closing
the connection.
Now for the \l{QTcpSocket::readyRead()}{readyRead()} alternative. This
signal is connected to \c Client::readFortune():
\snippet examples/network/fortuneclient/client.cpp 8
\codeline
\snippet examples/network/fortuneclient/client.cpp 10
The protocol is based on QDataStream, so we start by creating a stream
object, passing the socket to QDataStream's constructor. We then
explicitly set the protocol version of the stream to QDataStream::Qt_4_0
to ensure that we're using the same version as the fortune server, no
matter which version of Qt the client and server use.
Now, TCP is based on sending a stream of data, so we cannot expect to get
the entire fortune in one go. Especially on a slow network, the data can
be received in several small fragments. QTcpSocket buffers up all incoming
data and emits \l{QTcpSocket::readyRead()}{readyRead()} for every new
block that arrives, and it is our job to ensure that we have received all
the data we need before we start parsing. The server's response starts
with the size of the packet, so first we need to ensure that we can read
the size, then we will wait until QTcpSocket has received the full packet.
\snippet examples/network/fortuneclient/client.cpp 11
\codeline
\snippet examples/network/fortuneclient/client.cpp 12
We proceed by using QDataStream's streaming operator to read the fortune
from the socket into a QString. Once read, we can call QLabel::setText()
to display the fortune.
\sa {Fortune Server Example}, {Blocking Fortune Client Example}
*/
|