Qt --- network programming

Posted by philosophia on Tue, 21 Dec 2021 07:58:39 +0100

Network programming

The Qt network module provides us with classes for writing TCP / IP clients and servers. It provides lower level classes, such as QTcpSocket, QTcpServer and QUdpSocket representing low-level network concepts, and high-level classes such as QNetworkRequest, QNetworkReply and QNetworkAccessManager to perform network operations using common protocols. It also provides classes such as qnetworkconfiguration, qnetworkconfigurationmanager and QNetworkSession to realize bearer management.

To use Qt network module in the program, we need to add the following statement in the pro project configuration file.

QT += network

1, Get local network information

Why write the content to obtain local network information first? Before establishing network communication, we must at least obtain each other's IP address. In network applications, it is often necessary to use the host name, IP address, MAC address and other network information of the machine. Usually, you can view the relevant information by calling out the command line cmd window in Windows and entering ipconfig or using ifconfig command in Linux system. Here, we use Qt to make an interface and function that can be queried, In order to lay a simple foundation for the later network programming.

Qt provides QHostInfo and QNetworkInterface classes, which can be used for such information query. More related functions about QHostInfo and QNetworkInterface can be found in Qt's help document. Below, we will use relevant functions when writing code, with clear comments.

1. Application examples

Purpose of this example: learn how to obtain the information of all interfaces of the local network through QHostInfo and QNetworkInterface classes.

Example 07_ networkhostinfo , Get local network interface information (difficulty: average). The project path is Qt/2/07_networkhostinfo. This example obtains the local network interface information, prints it on the text browsing box, and clicks the button to obtain it directly. In order to clearly see the re acquisition process, this example clicks the obtain local information button, and then delays 1s to refresh the obtained information. Click another clear text information button to clear it The text content on the empty text browsing box.

Project document 07_ networkhostinfo. The code added to the first line of the pro file is as follows.

1 QT += core gui network
2
3 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
4
5 CONFIG += c++11
6
7 # The following define makes your compiler emit warnings if you use
8 # any Qt feature that has been marked deprecated (the exact warnings
9 # depend on your compiler). Please consult the documentation of the
10 # deprecated API in order to know how to port your code away from it.
11 DEFINES += QT_DEPRECATED_WARNINGS
12
13 # You can also make your code fail to compile if it uses deprecated APIs.
14 # In order to do so, uncomment the following line.
15 # You can also select to disable deprecated APIs only up to a certain
version of Qt.
16 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the
APIs deprecated before Qt 6.0.0
17
18 SOURCES += \
19 main.cpp \
20 mainwindow.cpp
21
22 HEADERS += \
23 mainwindow.h
24
25 # Default rules for deployment.
26 qnx: target.path = /tmp/$${TARGET}/bin
27 else: unix:!android: target.path = /opt/$${TARGET}/bin
28 !isEmpty(target.path): INSTALLS += target

In the header file "mainwindow.h", the specific code is as follows.

/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 07_networkhostinfo
* @brief mainwindow.h
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-10
*******************************************************************/
1 #ifndef MAINWINDOW_H
2 #define MAINWINDOW_H
3
4 #include <QMainWindow>
5 #include <QPushButton>
6 #include <QTextBrowser>
7 #include <QVBoxLayout>
8 #include <QHBoxLayout>
9 #include <QTimer>
10
11 class MainWindow : public QMainWindow
12 {
13 Q_OBJECT
14
15 public:
16 MainWindow(QWidget *parent = nullptr);
17 ~MainWindow();
18
19 private:
20 /* Click the get and empty text button */
21 QPushButton *pushButton[2];
22
23 /* The text browsing box is used to display the information of this machine */
24 QTextBrowser *textBrowser;
25
26 /* Horizontal Widget container and vertical Widget container*/
27 QWidget *hWidget;
28 QWidget *vWidget;
29
30 /* Horizontal and vertical layout */
31 QHBoxLayout *hBoxLayout;
32 QVBoxLayout *vBoxLayout;
33
34 /* timer */
35 QTimer *timer;
36
37 /* Get the network information of this machine. The return type is QString */
38 QString getHostInfo();
39
40 private slots:
41 /* Timer slot function, which is triggered regularly after clicking the button */
42 void timerTimeOut();
43
44 /* Display native information */
45 void showHostInfo();
46
47 /* Start timer */
48 void timerStart();
49
50 /* Clear textBrowser information */
51 void clearHostInfo();
52 };
53 #endif // MAINWINDOW_H
54

The header file mainly declares two buttons and a text browsing box. In addition, there is a timer that declares some slot functions, which is relatively simple.

In the source file "mainwindow.cpp", the specific code is as follows.

/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 07_networkhostinfo
* @brief mainwindow.cpp
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-10
*******************************************************************/
1 #include "mainwindow.h"
2 #include <QNetworkInterface>
3 #include <QHostInfo>
4 #include <QThread>
5 #include <QDebug>
6
7 MainWindow::MainWindow(QWidget *parent)
8 : QMainWindow(parent)
9 {
10 /* Set position and size */
11 this->setGeometry(0, 0, 800, 480);
12
13 /* Click the get local information button and the empty text button */
14 pushButton[0] = new QPushButton();
15 pushButton[1] = new QPushButton();
16
17 pushButton[0]->setText("Get native information");
18 pushButton[1]->setText("Clear text message");
19
20 /* The size of the button is adaptive to the text,
21 * Note that setSizePolicy needs to be used in the layout */
22 pushButton[0]->setSizePolicy(QSizePolicy::Fixed,
23 QSizePolicy::Fixed);
24 pushButton[1]->setSizePolicy(QSizePolicy::Fixed,
25 QSizePolicy::Fixed);
26
27 /* Horizontal and vertical widgets are used to add layouts */
28 hWidget = new QWidget();
29 vWidget = new QWidget();
30
31 /* Horizontal and vertical layout */
32 hBoxLayout = new QHBoxLayout();
33 vBoxLayout = new QVBoxLayout();
34
35 /* Text browsing box */
36 textBrowser = new QTextBrowser();
37
38 /* Add to horizontal layout */
39 hBoxLayout->addWidget(pushButton[0]);
40 hBoxLayout->addWidget(pushButton[1]);
41
42 /* Set the horizontal layout to the layout of the hWidget */
43 hWidget->setLayout(hBoxLayout);
44
45 /* Add a text browsing box and hWidget to the vertical layout */
46 vBoxLayout->addWidget(textBrowser);
47 vBoxLayout->addWidget(hWidget);
48
49 /* Set the vertical layout to the layout of the vWidget */
50 vWidget->setLayout(vBoxLayout);
51
52 /* Set vWidget as the central part */
53 setCentralWidget(vWidget);
54
55 /* timer initiated  */
56 timer = new QTimer();
57
58 /* Signal slot connection */
59 connect(pushButton[0], SIGNAL(clicked()),
60 this, SLOT(timerStart()));
61 connect(pushButton[1], SIGNAL(clicked()),
62 this, SLOT(clearHostInfo()));
63 connect(timer, SIGNAL(timeout()),
64 this, SLOT(timerTimeOut()));
65 }
66
67 MainWindow::~MainWindow()
68 {
69 }
70
71
72 void MainWindow::timerStart()
73 {
74 /* Empty text */
75 textBrowser->clear();
76
77 /* Timing 1s */
78 timer->start(1000);
79 }
80
81 void MainWindow::timerTimeOut()
82 {
83 /* Display native information */
84 showHostInfo();
85
86 /* Stop Timer  */
87 timer->stop();
88 }
89
90 QString MainWindow::getHostInfo()
91 {
92 /* Get the host name through the localHostName function of QHostInfo */
93 QString str = "Host name:" + QHostInfo::localHostName() + "\n";
94
95 /* Get all network interfaces,
96 * QNetworkInterface Class provides a list of host IP addresses and network interfaces */
97 QList<QNetworkInterface> list
98 = QNetworkInterface::allInterfaces();
99
100 /* Traversal list */
101 foreach (QNetworkInterface interface, list) {
102 str+= "Network card device:" + interface.name() + "\n";
103 str+= "MAC address:" + interface.hardwareAddress() + "\n";
104
105 /* QNetworkAddressEntry Class stores IP addresses, subnet masks, and broadcast addresses */
106 QList<QNetworkAddressEntry> entryList
107 = interface.addressEntries();
108
109 /* Traverse entryList */
110 foreach (QNetworkAddressEntry entry, entryList) {
111 /* Filter IPv6 addresses, leaving only IPv4 */
112 if (entry.ip().protocol() ==
113 QAbstractSocket::IPv4Protocol) {
114 str+= "IP address:" + entry.ip().toString() + "\n";
115 str+= "Subnet mask:" + entry.netmask().toString() + "\n";
116 str+= "Broadcast address:" + entry.broadcast().toString() + "\n\n";
117 }
118 }
119 }
120
121 /* Return network information */
122 return str;
123 }
124
125 void MainWindow::showHostInfo()
126 {
127 /* Get the local information and display it in textBrowser */
128 textBrowser->insertPlainText(getHostInfo());
129 }
130
131 void MainWindow::clearHostInfo()
132 {
133 /* Judge whether textBrowser is empty. If not, clear the text */
134 if (!textBrowser->toPlainText().isEmpty())
135
136 /* Empty text */
137 textBrowser->clear();
138 }

Lines 90 to 123 are the most important code in this example.
Line 93, get the host name through the localHostName function of QHostInfo.
In lines 97 ~ 98, get the network interface list through QNetworkInterface::allInterfaces(). The list class stores the IP address, subnet mask and broadcast address. If we print out the list with the qDebug() function, we can find that we have obtained all the network information. We need to extract the network information in the network and use QNetworkAddressEntry.
In lines 106-107, use QNetworkAddressEntry to obtain all entries from the interface interface using the function addressEntries(). You can use the object entry of QNetworkAddressEntry to obtain the IP address, subnet mask and broadcast address.
Lines 110 ~ 118, because the entries obtained may have two IPS under a QNetworkInterface, namely ipv4 and ipv6. IP () is used here Protocol () to determine the type of protocol, leaving only the information of ipv4 type. Filtering information is often needed when we write programs.

2. Program running effect

Click to get local information, Print out the local network information in the text browsing box (including host name, network card name, ip address, etc.). IPv6 information is filtered out here. Usually, one network card has two ip addresses, one is ipv4 and the other is IPv6. The network card device lo below is the local loopback network card. The other ens33 is the network card of the virtual machine, which is virtualized by VMware. Clicking empty text information will empty the text browsing box Network information in.

2, TCP communication

1. Introduction to TCP

The full name of TCP (Transmission Control Protocol) is Transmission Control Protocol. It is a connection oriented, reliable and byte stream based transport layer communication protocol.

TCP communication must first establish a TCP connection. The communication end is divided into client and server. The server monitors whether a client connection arrives by listening to a port. If a connection arrives, it establishes a new socket connection; The client connects to the server through ip and port. After the connection is successfully established, data can be sent and received. It should be noted that in Qt, Qt treats sockets as input and output streams. Data is sent and received through read() and write(), which should be distinguished from our common send() and recv().

The communication diagram between TCP client and server is as follows.

2. TCP server application example

Purpose of this example: to understand the use of TCP server.

Example 08_ tcpServer, TCP server (difficulty: average). The project path is Qt/2/08_tcpserver. The general process of this example first obtains the local IP address. Create a tcpSocket socket and a tcpServer server. Click listen to listen to the local host IP address and port, and wait for the connection of the server to be served. This program needs to be used in combination with the client.

Project document 08_ tcpserver. The code added to the first line of the pro file is as follows.

1 QT += core gui network
2
3 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
4
5 CONFIG += c++11
6
7 # The following define makes your compiler emit warnings if you use
8 # any Qt feature that has been marked deprecated (the exact warnings
9 # depend on your compiler). Please consult the documentation of the
10 # deprecated API in order to know how to port your code away from it.
11 DEFINES += QT_DEPRECATED_WARNINGS
12
13 # You can also make your code fail to compile if it uses deprecated APIs.
14 # In order to do so, uncomment the following line.
15 # You can also select to disable deprecated APIs only up to a certain
version of Qt.
16 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the
APIs deprecated before Qt 6.0.0
17
18 SOURCES += \
19 main.cpp \
20 mainwindow.cpp
21
22 HEADERS += \
23 mainwindow.h
24
25 # Default rules for deployment.
26 qnx: target.path = /tmp/$${TARGET}/bin
27 else: unix:!android: target.path = /opt/$${TARGET}/bin
28 !isEmpty(target.path): INSTALLS += target

In the header file "mainwindow.h", the specific code is as follows.

/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 08_tcpserver
* @brief mainwindow.h
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-13
*******************************************************************/
1 #ifndef MAINWINDOW_H
2 #define MAINWINDOW_H
3
4 #include <QMainWindow>
5 #include <QTcpServer>
6 #include <QTcpSocket>
7 #include <QVBoxLayout>
8 #include <QHBoxLayout>
9 #include <QPushButton>
10 #include <QTextBrowser>
11 #include <QLabel>
12 #include <QComboBox>
13 #include <QSpinBox>
14 #include <QHostInfo>
15 #include <QLineEdit>
16 #include <QNetworkInterface>
17 #include <QDebug>
18
19 class MainWindow : public QMainWindow
20 {
21 Q_OBJECT
22
23 public:
24 MainWindow(QWidget *parent = nullptr);
25 ~MainWindow();
26
27 private:
28 /* tcp The server */
29 QTcpServer *tcpServer;
30
31 /* Communication socket */
32 QTcpSocket *tcpSocket;
33
34 /* Button */
35 QPushButton *pushButton[4];
36
37 /* Label text */
38 QLabel *label[2];
39
40 /* Horizontal container */
41 QWidget *hWidget[3];
42
43 /* Horizontal layout */
44 QHBoxLayout *hBoxLayout[3];
45
46 /* Vertical container */
47 QWidget *vWidget;
48
49 /* Vertical layout */
50 QVBoxLayout *vBoxLayout;
51
52 /* Text browsing box */
53 QTextBrowser *textBrowser;
54
55 /* Used to display the local ip address */
56 QComboBox *comboBox;
57
58 /* Used to select ports */
59 QSpinBox *spinBox;
60
61 /* Text input box */
62 QLineEdit *lineEdit;
63
64 /* Store local ip address list */
65 QList<QHostAddress> IPlist;
66
67 /* Get all local ip addresses */
68 void getLocalHostIP();
69
70 private slots:
71 /* Client connection processing slot function */
72 void clientConnected();
73
74 /* Start listening slot function */
75 void startListen();
76
77 /* Stop listening slot function */
78 void stopListen();
79
80 /* Content when text box is cleared */
81 void clearTextBrowser();
82
83 /* Message received */
84 void receiveMessages();
85
86 /* send message */
87 void sendMessages();
88
89 /* Connection state change slot function */
90 void socketStateChange(QAbstractSocket::SocketState);
91 };
92 #endif // MAINWINDOW_H

The header file mainly declares the elements used for the interface and some slot functions. The key point is to declare tcpServer and tcpSocket.

In the source file "mainwindow.cpp", the specific code is as follows.

/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 08_tcpserver
* @brief mainwindow.cpp
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-13
*******************************************************************/
1 #include "mainwindow.h"
2
3 MainWindow::MainWindow(QWidget *parent)
4 : QMainWindow(parent)
5 {
6 /* Set the position and size of the main form */
7 this->setGeometry(0, 0, 800, 480);
8
9 /* Instantiate tcp server and tcp socket */
10 tcpServer = new QTcpServer(this);
11 tcpSocket = new QTcpSocket(this);
12
13 /* Start listening button */
14 pushButton[0] = new QPushButton();
15 /* Stop listening button */
16 pushButton[1] = new QPushButton();
17 /* Empty chat text button */
18 pushButton[2] = new QPushButton();
19 /* Send Message button */
20 pushButton[3] = new QPushButton();
21
22 /* Horizontal layout I */
23 hBoxLayout[0] = new QHBoxLayout();
24 /* Horizontal layout II */
25 hBoxLayout[1] = new QHBoxLayout();
26 /* Horizontal layout III */
27 hBoxLayout[2] = new QHBoxLayout();
28 /* Horizontal layout IV */
29 hBoxLayout[3] = new QHBoxLayout();
30
31 /* Horizontal container I */
32 hWidget[0] = new QWidget();
33 /* Horizontal container II */
34 hWidget[1] = new QWidget();
35 /* Horizontal container III */
36 hWidget[2] = new QWidget();
37
38 vWidget = new QWidget();
39 vBoxLayout = new QVBoxLayout();
40
41 /* Tag instantiation */
42 label[0] = new QLabel();
43 label[1] = new QLabel();
44
45 lineEdit = new QLineEdit();
46 comboBox = new QComboBox();
47 spinBox = new QSpinBox();
48 textBrowser = new QTextBrowser();
49
50 label[0]->setText("monitor IP Address:");
51 label[1]->setText("Listening port:");
52
53 /* Sets the label size to adapt to the text size */
54 label[0]->setSizePolicy(QSizePolicy::Fixed,
55 QSizePolicy::Fixed);
56 label[1]->setSizePolicy(QSizePolicy::Fixed,
57 QSizePolicy::Fixed);
58
59 /* Set the range of port number. Be careful not to conflict with the used port number of the host */
60 spinBox->setRange(10000, 99999);
61
62 pushButton[0]->setText("Start listening");
63 pushButton[1]->setText("Stop listening");
64 pushButton[2]->setText("Empty text");
65 pushButton[3]->setText("send message");
66
67 /* Set stop listening status not available */
68 pushButton[1]->setEnabled(false);
69
70 /* Sets the default text for the input box */
71 lineEdit->setText("www.openedv.com Punctual Atomic Forum");
72
73 /* Horizontal layout - add content */
74 hBoxLayout[0]->addWidget(pushButton[0]);
75 hBoxLayout[0]->addWidget(pushButton[1]);
76 hBoxLayout[0]->addWidget(pushButton[2]);
77
78 /* Set the layout of horizontal container 1 as horizontal layout 1 */
79 hWidget[0]->setLayout(hBoxLayout[0]);
80
81 /* Add content to horizontal layout II */
82 hBoxLayout[1]->addWidget(label[0]);
83 hBoxLayout[1]->addWidget(comboBox);
84 hBoxLayout[1]->addWidget(label[1]);
85 hBoxLayout[1]->addWidget(spinBox);
86
87 /* Set the layout of horizontal container 2 as horizontal layout 2 */
88 hWidget[1]->setLayout(hBoxLayout[1]);
89
90 /* Add content to horizontal layout III */
91 hBoxLayout[2]->addWidget(lineEdit);
92 hBoxLayout[2]->addWidget(pushButton[3]);
93
94 /* Set the layout of horizontal container 3 as horizontal layout 1 */
95 hWidget[2]->setLayout(hBoxLayout[2]);
96
97 /* Add content to vertical layout */
98 vBoxLayout->addWidget(textBrowser);
99 vBoxLayout->addWidget(hWidget[1]);
100 vBoxLayout->addWidget(hWidget[0]);
101 vBoxLayout->addWidget(hWidget[2]);
102
103 /* Set the layout of the vertical container to vertical layout */
104 vWidget->setLayout(vBoxLayout);
105
106 /* Center display */
107 setCentralWidget(vWidget);
108
109 /* Get local ip */
110 getLocalHostIP();
111
112 /* Signal slot connection */
113 connect(pushButton[0], SIGNAL(clicked()),
114 this, SLOT(startListen()));
115 connect(pushButton[1], SIGNAL(clicked()),
116 this, SLOT(stopListen()));
117 connect(pushButton[2], SIGNAL(clicked()),
118 this, SLOT(clearTextBrowser()));
119 connect(pushButton[3], SIGNAL(clicked()),
120 this, SLOT(sendMessages()));
121 connect(tcpServer, SIGNAL(newConnection()),
122 this, SLOT(clientConnected()));
123 }
124
125 MainWindow::~MainWindow()
126 {
127 }
128
129 /* New client connection */
130 void MainWindow::clientConnected()
131 {
132 /* Gets the socket of the client */
133 tcpSocket = tcpServer->nextPendingConnection();
134 /* ip information of client */
135 QString ip = tcpSocket->peerAddress().toString();
136 /* Port information of the client */
137 qint16 port = tcpSocket->peerPort();
138 /* The connection information of the client is displayed in the text browsing box */
139 textBrowser->append("Client connected");
140 textBrowser->append("client ip address:"
141 + ip);
142 textBrowser->append("Client port:"
143 + QString::number(port));
144
145 connect(tcpSocket, SIGNAL(readyRead()),
146 this, SLOT(receiveMessages()));
147 connect(tcpSocket,
148 SIGNAL(stateChanged(QAbstractSocket::SocketState)),
149 this,
150 SLOT(socketStateChange(QAbstractSocket::SocketState)));
151 }
152
153 /* Get local IP */
154 void MainWindow::getLocalHostIP()
155 {
156 // /*Gets the name of the host*/
157 // QString hostName = QHostInfo::localHostName();
158
159 // /*Host information*/
160 // QHostInfo hostInfo = QHostInfo::fromName(hostName);
161
162 // /*ip list. addresses returns the ip address list. Note that the host should be able to obtain it from the router
163 // *IP, otherwise an empty list may be returned (ubuntu can only obtain loopback IP with this method)*/
164 // IPlist = hostInfo.addresses();
165 // qDebug()<<IPlist<<endl;
166
167 // /*Traverse IPlist*/
168 // foreach (QHostAddress ip, IPlist) {
169 // if (ip.protocol() == QAbstractSocket::IPv4Protocol)
170 // comboBox->addItem(ip.toString());
171 // }
172
173 /* Get all network interfaces,
174 * QNetworkInterface Class provides a list of host IP addresses and network interfaces */
175 QList<QNetworkInterface> list
176 = QNetworkInterface::allInterfaces();
177
178 /* Traversal list */
179 foreach (QNetworkInterface interface, list) {
180
181 /* QNetworkAddressEntry Class stores IP addresses, subnet masks, and broadcast addresses */
182 QList<QNetworkAddressEntry> entryList
183 = interface.addressEntries();
184
185 /* Traverse entryList */
186 foreach (QNetworkAddressEntry entry, entryList) {
187 /* Filter IPv6 addresses, leaving only IPv4 */
188 if (entry.ip().protocol() ==
189 QAbstractSocket::IPv4Protocol) {
190 comboBox->addItem(entry.ip().toString());
191 /* Add to IP list */
192 IPlist<<entry.ip();
193 }
194 }
195 }
196 }
197
198 /* Start listening */
199 void MainWindow::startListen()
200 {
201 /* You need to judge whether the current host has an IP item */
202 if (comboBox->currentIndex() != -1) {
203 qDebug()<<"start listen"<<endl;
204 tcpServer->listen(IPlist[comboBox->currentIndex()],
205 spinBox->value());
206
207 /* Set the status of the button and drop-down list box */
208 pushButton[0]->setEnabled(false);
209 pushButton[1]->setEnabled(true);
210 comboBox->setEnabled(false);
211 spinBox->setEnabled(false);
212
213 /* The server is displayed in the text browsing box */
214 textBrowser->append("The server IP Address:"
215 + comboBox->currentText());
216 textBrowser->append("Listening port:"
217 + spinBox->text());
218 }
219 }
220
221 /* Stop listening */
222 void MainWindow::stopListen()
223 {
224 qDebug()<<"stop listen"<<endl;
225 /* Stop listening */
226 tcpServer->close();
227
228 /* If it is connected, it should also be disconnected. If it is disconnected, the client can continue to send information,
229 * Because the socket is not disconnected, it is still listening to the last port */
230 if (tcpSocket->state() == tcpSocket->ConnectedState)
231 tcpSocket->disconnectFromHost();
232
233 /* Set the status of the button and drop-down list box */
234 pushButton[1]->setEnabled(false);
235 pushButton[0]->setEnabled(true);
236 comboBox->setEnabled(true);
237 spinBox->setEnabled(true);
238
239 /* Add the stop listening information to the text browsing box */
240 textBrowser->append("Listening port stopped:"
241 + spinBox->text());
242 }
243
244 /* Clear the contents of the text browsing box */
245 void MainWindow::clearTextBrowser()
246 {
247 /* Clear the contents of the text browser */
248 textBrowser->clear();
249 }
250
251 /* The server receives the message */
252 void MainWindow::receiveMessages()
253 {
254 /* Read received messages */
255 QString messages = "client:" + tcpSocket->readAll();
256 textBrowser->append(messages);
257 }
258
259 /* The server sends a message */
260 void MainWindow::sendMessages()
261 {
262 if(NULL == tcpSocket)
263 return;
264
265 /* If already connected */
266 if(tcpSocket->state() == tcpSocket->ConnectedState) {
267 /* send message */
268 tcpSocket->write(lineEdit->text().toUtf8().data());
269
270 /* Insert the sent message at the server */
271 textBrowser->append("Server:" + lineEdit->text());
272 }
273 }
274
275 /* Server state change */
276 void MainWindow::socketStateChange(QAbstractSocket::SocketState
state)
277 {
278 switch (state) {
279 case QAbstractSocket::UnconnectedState:
280 textBrowser->append("scoket Status: UnconnectedState");
281 break;
282 case QAbstractSocket::ConnectedState:
283 textBrowser->append("scoket Status: ConnectedState");
284 break;
285 case QAbstractSocket::ConnectingState:
286 textBrowser->append("scoket Status: ConnectingState");
287 break;
288 case QAbstractSocket::HostLookupState:
289 textBrowser->append("scoket Status: HostLookupState");
290 break;
291 case QAbstractSocket::ClosingState:
292 textBrowser->append("scoket Status: ClosingState");
293 break;
294 case QAbstractSocket::ListeningState:
295 textBrowser->append("scoket Status: ListeningState");
296 break;
297 case QAbstractSocket::BoundState:
298 textBrowser->append("scoket Status: BoundState");
299 break;
300 default:
301 break;
302 }
303 }

The above code is mainly for the server to start listening. If a client is connected to the server, it will send a newConnection() signal, and also connect to the signal and slot function receiving the message. Click the send message button to send a message using tcpSocket. Note that both sending and receiving messages are performed through tcpSocket's read() and write().

3. TCP client application instance

Purpose of this example: to understand the use of TCP clients.

Example 09_tcpclient, TCP client (difficulty: average). The project path is QT / 2 / 09_; tcpclient. The general process of this example is: first obtain the local IP address. Create a tcpSocket socket, and then use the tcpSocket socket to connect the host IP address and port of the server using the connectToHost function to communicate with each other.

Project document 08_ tcpserver. The code added to the first line of the pro file is as follows.

1 QT += core gui network
2
3 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
4
5 CONFIG += c++11
6
7 # The following define makes your compiler emit warnings if you use
8 # any Qt feature that has been marked deprecated (the exact warnings
9 # depend on your compiler). Please consult the documentation of the
10 # deprecated API in order to know how to port your code away from it.
11 DEFINES += QT_DEPRECATED_WARNINGS
12
13 # You can also make your code fail to compile if it uses deprecated APIs.
14 # In order to do so, uncomment the following line.
15 # You can also select to disable deprecated APIs only up to a certain
version of Qt.
16 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the
APIs deprecated before Qt 6.0.0
17
18 SOURCES += \
19 main.cpp \
20 mainwindow.cpp
21
22 HEADERS += \
23 mainwindow.h
24
25 # Default rules for deployment.
26 qnx: target.path = /tmp/$${TARGET}/bin
27 else: unix:!android: target.path = /opt/$${TARGET}/bin
28 !isEmpty(target.path): INSTALLS += target

In the header file "mainwindow.h", the specific code is as follows.

/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 09_tcpclient
* @brief mainwindow.h
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-13
*******************************************************************/
1 #ifndef MAINWINDOW_H
2 #define MAINWINDOW_H
3
4 #include <QMainWindow>
5 #include <QTcpServer>
6 #include <QTcpSocket>
7 #include <QVBoxLayout>
8 #include <QHBoxLayout>
9 #include <QPushButton>
10 #include <QTextBrowser>
11 #include <QLabel>
12 #include <QComboBox>
13 #include <QSpinBox>
14 #include <QHostInfo>
15 #include <QLineEdit>
16 #include <QNetworkInterface>
17 #include <QDebug>
18
19 class MainWindow : public QMainWindow
20 {
21 Q_OBJECT
22
23 public:
24 MainWindow(QWidget *parent = nullptr);
25 ~MainWindow();
26
27 private:
28 /* Communication socket */
29 QTcpSocket *tcpSocket;
30
31 /* Button */
32 QPushButton *pushButton[4];
33
34 /* Label text */
35 QLabel *label[2];
36
37 /* Horizontal container */
38 QWidget *hWidget[3];
39
40 /* Horizontal layout */
41 QHBoxLayout *hBoxLayout[3];
42
43 /* Vertical container */
44 QWidget *vWidget;
45
46 /* Vertical layout */
47 QVBoxLayout *vBoxLayout;
48
49 /* Text browsing box */
50 QTextBrowser *textBrowser;
51
52 /* Used to display the local ip address */
53 QComboBox *comboBox;
54
55 /* Used to select ports */
56 QSpinBox *spinBox;
57
58 /* Text input box */
59 QLineEdit *lineEdit;
60
61 /* Store local ip address list */
62 QList<QHostAddress> IPlist;
63
64 /* Get all local ip addresses */
65 void getLocalHostIP();
66
67 private slots:
68 /* connect */
69 void toConnect();
70
71 /* Disconnect */
72 void toDisConnect();
73
74 /* Connected */
75 void connected();
76
77 /* Disconnected */
78 void disconnected();
79
80 /* Content when text box is cleared */
81 void clearTextBrowser();
82
83 /* Message received */
84 void receiveMessages();
85
86 /* send message */
87 void sendMessages();
88
89 /* Connection state change slot function */
90 void socketStateChange(QAbstractSocket::SocketState);
91 };
92 #endif // MAINWINDOW_H

The header file mainly declares the elements used for the interface and some slot functions. The point is to declare tcpSocket.

In the source file "mainwindow.cpp", the specific code is as follows.

/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 09_tcpclient
* @brief mainwindow.cpp
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-13
*******************************************************************/
1 #include "mainwindow.h"
2
3 MainWindow::MainWindow(QWidget *parent)
4 : QMainWindow(parent)
5 {
6 /* Set the position and size of the main form */
7 this->setGeometry(0, 0, 800, 480);
8
9 /* tcp socket */
10 tcpSocket = new QTcpSocket(this);
11
12 /* Start listening button */
13 pushButton[0] = new QPushButton();
14 /* Stop listening button */
15 pushButton[1] = new QPushButton();
16 /* Empty chat text button */
17 pushButton[2] = new QPushButton();
18 /* Send Message button */
19 pushButton[3] = new QPushButton();
20
21 /* Horizontal layout I */
22 hBoxLayout[0] = new QHBoxLayout();
23 /* Horizontal layout II */
24 hBoxLayout[1] = new QHBoxLayout();
25 /* Horizontal layout III */
26 hBoxLayout[2] = new QHBoxLayout();
27 /* Horizontal layout IV */
28 hBoxLayout[3] = new QHBoxLayout();
29
30 /* Horizontal container I */
31 hWidget[0] = new QWidget();
32 /* Horizontal container II */
33 hWidget[1] = new QWidget();
34 /* Horizontal container III */
35 hWidget[2] = new QWidget();
36
37
38 vWidget = new QWidget();
39 vBoxLayout = new QVBoxLayout();
40
41 /* Tag instantiation */
42 label[0] = new QLabel();
43 label[1] = new QLabel();
44
45 lineEdit = new QLineEdit();
46 comboBox = new QComboBox();
47 spinBox = new QSpinBox();
48 textBrowser = new QTextBrowser();
49
50 label[0]->setText("Server address:");
51 label[1]->setText("Server port:");
52
53 /* Sets the label size to adapt to the text size */
54 label[0]->setSizePolicy(QSizePolicy::Fixed,
55 QSizePolicy::Fixed);
56 label[1]->setSizePolicy(QSizePolicy::Fixed,
57 QSizePolicy::Fixed);
58
59 /* Set the range of port number. Be careful not to conflict with the used port number of the host */
60 spinBox->setRange(10000, 99999);
61
62 pushButton[0]->setText("Connect server");
63 pushButton[1]->setText("Disconnect");
64 pushButton[2]->setText("Empty text");
65 pushButton[3]->setText("send message");
66
67 /* Set stop listening status not available */
68 pushButton[1]->setEnabled(false);
69
70 /* Sets the default text for the input box */
71 lineEdit->setText("Guangzhou Xingyi Electronic Technology Co., Ltd");
72
73 /* Horizontal layout - add content */
74 hBoxLayout[0]->addWidget(pushButton[0]);
75 hBoxLayout[0]->addWidget(pushButton[1]);
76 hBoxLayout[0]->addWidget(pushButton[2]);
77
78 /* Set the layout of the horizontal container to horizontal layout I */
79 hWidget[0]->setLayout(hBoxLayout[0]);
80
81 hBoxLayout[1]->addWidget(label[0]);
82 hBoxLayout[1]->addWidget(comboBox);
83 hBoxLayout[1]->addWidget(label[1]);
84 hBoxLayout[1]->addWidget(spinBox);
85
86 /* Set the layout of the horizontal container to horizontal layout II */
87 hWidget[1]->setLayout(hBoxLayout[1]);
88
89 /* Add content to horizontal layout III */
90 hBoxLayout[2]->addWidget(lineEdit);
91 hBoxLayout[2]->addWidget(pushButton[3]);
92
93 /* Set the layout of horizontal container 3 as horizontal layout 1 */
94 hWidget[2]->setLayout(hBoxLayout[2]);
95
96 /* Add content to vertical layout */
97 vBoxLayout->addWidget(textBrowser);
98 vBoxLayout->addWidget(hWidget[1]);
99 vBoxLayout->addWidget(hWidget[0]);
100 vBoxLayout->addWidget(hWidget[2]);
101
102 /* Set the layout of the vertical container to vertical layout */
103 vWidget->setLayout(vBoxLayout);
104
105 /* Center display */
106 setCentralWidget(vWidget);
107
108 /* Get local ip */
109 getLocalHostIP();
110
111 /* Signal slot connection */
112 connect(pushButton[0], SIGNAL(clicked()),
113 this, SLOT(toConnect()));
114 connect(pushButton[1], SIGNAL(clicked()),
115 this, SLOT(toDisConnect()));
116 connect(pushButton[2], SIGNAL(clicked()),
117 this, SLOT(clearTextBrowser()));
118 connect(pushButton[3], SIGNAL(clicked()),
119 this, SLOT(sendMessages()));
120 connect(tcpSocket, SIGNAL(connected()),
121 this, SLOT(connected()));
122 connect(tcpSocket, SIGNAL(disconnected()),
123 this, SLOT(disconnected()));
124 connect(tcpSocket, SIGNAL(readyRead()),
125 this, SLOT(receiveMessages()));
126 connect(tcpSocket,
127 SIGNAL(stateChanged(QAbstractSocket::SocketState)),
128 this,
129 SLOT(socketStateChange(QAbstractSocket::SocketState)));
130 }
131
132 MainWindow::~MainWindow()
133 {
134 }
135
136 void MainWindow::toConnect()
137 {
138 /* If the connection status is not connected */
139 if (tcpSocket->state() != tcpSocket->ConnectedState) {
140 /* Specify the IP address and port connection */
141 tcpSocket->connectToHost(IPlist[comboBox->currentIndex()],
142 spinBox->value());
143 }
144 }
145
146 void MainWindow::toDisConnect()
147 {
148 /* Disconnect */
149 tcpSocket->disconnectFromHost();
150
151 /* Close socket*/
152 tcpSocket->close();
153 }
154
155 void MainWindow::connected()
156 {
157 /* Show connected */
158 textBrowser->append("Connected to the server");
159
160 /* Set the status of the button and drop-down list box */
161 pushButton[0]->setEnabled(false);
162 pushButton[1]->setEnabled(true);
163 comboBox->setEnabled(false);
164 spinBox->setEnabled(false);
165 }
166
167 void MainWindow::disconnected()
168 {
169 /* Show disconnected */
170 textBrowser->append("The server has been disconnected");
171
172 /* Set the status of the button and drop-down list box */
173 pushButton[1]->setEnabled(false);
174 pushButton[0]->setEnabled(true);
175 comboBox->setEnabled(true);
176 spinBox->setEnabled(true);
177 }
178
179 /* Get local IP */
180 void MainWindow::getLocalHostIP()
181 {
182 // /*Gets the name of the host*/
183 // QString hostName = QHostInfo::localHostName();
184
185 // /*Host information*/
186 // QHostInfo hostInfo = QHostInfo::fromName(hostName);
187
188 // /*ip list. addresses returns the ip address list. Note that the host should be able to obtain it from the router
189 // *IP, otherwise an empty list may be returned (ubuntu can only obtain loopback IP with this method)*/
190 // IPlist = hostInfo.addresses();
191 // qDebug()<<IPlist<<endl;
192
193 // /*Traverse IPlist*/
194 // foreach (QHostAddress ip, IPlist) {
195 // if (ip.protocol() == QAbstractSocket::IPv4Protocol)
196 // comboBox->addItem(ip.toString());
197 // }
198
199 /* Get all network interfaces,
200 * QNetworkInterface Class provides a list of host IP addresses and network interfaces */
201 QList<QNetworkInterface> list
202 = QNetworkInterface::allInterfaces();
203
204 /* Traversal list */
205 foreach (QNetworkInterface interface, list) {
206
207 /* QNetworkAddressEntry Class stores IP addresses, subnet masks, and broadcast addresses */
208 QList<QNetworkAddressEntry> entryList
209 = interface.addressEntries();
210
211 /* Traverse entryList */
212 foreach (QNetworkAddressEntry entry, entryList) {
213 /* Filter IPv6 addresses, leaving only IPv4 */
214 if (entry.ip().protocol() ==
215 QAbstractSocket::IPv4Protocol) {
216 comboBox->addItem(entry.ip().toString());
217 /* Add to IP list */
218 IPlist<<entry.ip();
219 }
220 }
221 }
222 }
223
224 /* Clear the contents of the text browsing box */
225 void MainWindow::clearTextBrowser()
226 {
227 /* Clear the contents of the text browser */
228 textBrowser->clear();
229 }
230
231 /* Client receives message */
232 void MainWindow::receiveMessages()
233 {
234 /* Read received messages */
235 QString messages = tcpSocket->readAll();
236 textBrowser->append("Server:" + messages);
237 }
238
239 /* Client send message */
240 void MainWindow::sendMessages()
241 {
242 if(NULL == tcpSocket)
243 return;
244
245 if(tcpSocket->state() == tcpSocket->ConnectedState) {
246 /* The client displays the sent message */
247 textBrowser->append("client:" + lineEdit->text());
248
249 /* send message */
250 tcpSocket->write(lineEdit->text().toUtf8().data());
251 }
252 }
253
254 /* Client state change */
255 void MainWindow::socketStateChange(QAbstractSocket::SocketState
state)
256 {
257 switch (state) {
258 case QAbstractSocket::UnconnectedState:
259 textBrowser->append("scoket Status: UnconnectedState");
260 break;
261 case QAbstractSocket::ConnectedState:
262 textBrowser->append("scoket Status: ConnectedState");
263 break;
264 case QAbstractSocket::ConnectingState:
265 textBrowser->append("scoket Status: ConnectingState");
266 break;
267 case QAbstractSocket::HostLookupState:
268 textBrowser->append("scoket Status: HostLookupState");
269 break;
270 case QAbstractSocket::ClosingState:
271 textBrowser->append("scoket Status: ClosingState");
272 break;
273 case QAbstractSocket::ListeningState:
274 textBrowser->append("scoket Status: ListeningState");
275 break;
276 case QAbstractSocket::BoundState:
277 textBrowser->append("scoket Status: BoundState");
278 break;
279 default:
280 break;
281 }
282 }

The above code is mainly used by the client to connect to the server through the IP address and port. If the connection is successful, it will transmit the connected () signal and connect to the signal and slot function receiving the message. Click the send message button to send a message using tcpSocket. Note that both sending and receiving messages are performed through tcpSocket's read() and write().

4. Program operation effect

After opening the server, you need to select the IP address and port to listen to locally (note that you should not select the port to listen to and the port already used by the local host, so the editor sets the port number too large. To view the port number already used locally, you can use the netstat command.)

After starting the client, select the IP address of the server to be connected and the port on which the server listens. Click Connect to send messages to each other.

Note that both the server and the client have selected local loopback IP 127.0 in this example 0.1 test. You can also select other local IP addresses for testing.

TCP server:

TCP client:

3, UDP communication

1. Introduction to UDP

UDP User datagram protocol (User Datagram Protocol) is a lightweight, unreliable, datagram oriented connectionless protocol. QQ used in our daily life uses UDP protocol to send messages during chat. Because QQ has many users, most of the messages sent are short messages, which require timely response and security requirements Use UDP protocol when it is not very high. However, QQ does not completely use UDP protocol. For example, when transmitting files, we will choose TCP protocol to ensure the correct transmission of files. Like QQ voice and QQ video calls, the advantages of UDP are very prominent. When choosing to use the protocol, you must be careful when choosing UDP. In the environment of unsatisfactory network quality, UDP packet loss will be more serious. However, due to the characteristics of UDP: it does not belong to the connection protocol, so it has the advantages of low resource consumption and fast processing speed. Therefore, UDP is usually used more for audio, video and ordinary data transmission, because even if they occasionally lose one or two packets, they will not have a great impact on the reception results.

The QUdpSocket class provides a UDP socket. QUdpSocket is a subclass of QAbstractSocket, which allows sending and receiving UDP datagrams. The most common way to use this class is to use bind() to bind to an address and port, then call writeDatagram() and readDatagram() / receiveDatagram() to transmit data. Note that the transmitted data is generally less than 512 bytes. If we send more than 512 bytes of data, even if we send it successfully, it will be fragmented (divided into small fragments) at the IP layer.

If you want to use the standard QIODevice functions read(), readLine(), write(), etc., you must first connect the socket directly to the peer by calling connectToHost(). Each time a datagram is written to the network, the socket issues a byteswriten() signal.

If you just want to send datagrams, you don't need to call bind(). The readyRead() signal is issued when the datagram arrives.
In this case, hasPendingDatagrams() returns true. Call pendingDatagramSize() to get the size of the first datagram to be processed, and call readDatagram() or receiveDatagram() to read it. Note: when you receive the readyRead() signal, an incoming datagram should be read, otherwise the signal will not be sent to the next datagram.

UDP communication diagram is as follows. The focus is on the QUdpSocket class, which has provided us with the basis of UDP communication

There are three modes of UDP message transmission: unicast, broadcast and multicast.

Unicast: unicast is used for end-to-end communication between two hosts. You need to know each other's IP address and port.
 broadcast: the difference between broadcast UDP and unicast UDP is the IP address. Broadcast generally uses broadcast address 255.255.255.255 to send messages to the same broadcast (that is, the same network segment in the LAN) each host on the network. It is worth emphasizing that the local broadcast information will not be forwarded by the router. Of course, this is very easy to understand, because if the router forwards the broadcast information, it is bound to cause network paralysis. This is why the designer of the IP protocol deliberately did not define the Internet wide broadcast mechanism Address is usually used to exchange status information between players in the same local network in online games. In fact, broadcasting, as its name implies, is to talk to all people in the LAN, but broadcasting still needs to indicate the recipient's port number, because it is impossible for all the recipient's ports to listen to broadcasting.
 multicast: Multicast (multicast), also known as "multicast" , the hosts of the same service type in the network are logically grouped. When sending and receiving data, their data is only carried out in the same packet. Other hosts do not join this packet and cannot send and receive corresponding data. When broadcasting on WAN, the switches and routers only copy and forward data to the host that needs to obtain data. The host can request the router to join or exit a group. The routers and switches in the network selectively copy and transmit data, and only transmit the data to the hosts in the group. This function of multicast can send data to multiple hosts at one time, and ensure that it does not affect other communications of other hosts that do not need to (not join the group).

Note: unicast and multicast are allowed to be transmitted on the wide area network, that is, the Internet, while broadcasting is only on the same local area
Online.

2. UDP unicast and broadcast

The difference between broadcast UDP and unicast UDP is that the IP address is different, so our example can be written as one. We can understand that unicast actually corresponds to one-to-one in communication, and broadcast is one to many (many, here refers to all hosts in the broadcast address).

1. Application examples

Purpose of this example: to understand the use of QUdpSocket unicast and broadcast.

Example 10_ udp_ unicast_ broadcast, UDP unicast and broadcast applications (difficulty: average). The project path is Qt/2/10_udp_unicast_broadcast. The general process of this example first obtains the local IP address. Create a UDP socket socket, and then bind the port of the local host (that is, the listening port). We can use the read-write functions readDatagram and writeDatagram provided by the QUdpSocket class to know the target IP address and port to receive and send messages.

Project document 10_ udp_ unicast_ broadcast. The code added to the first line of the pro file is as follows.

1 QT += core gui network
2
3 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
4
5 CONFIG += c++11
6
7 # The following define makes your compiler emit warnings if you use
8 # any Qt feature that has been marked deprecated (the exact warnings
9 # depend on your compiler). Please consult the documentation of the
10 # deprecated API in order to know how to port your code away from it.
11 DEFINES += QT_DEPRECATED_WARNINGS
12
13 # You can also make your code fail to compile if it uses deprecated APIs.
14 # In order to do so, uncomment the following line.
15 # You can also select to disable deprecated APIs only up to a certain
version of Qt.
16 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the
APIs deprecated before Qt 6.0.0
17
18 SOURCES += \
19 main.cpp \
20 mainwindow.cpp
21
22 HEADERS += \
23 mainwindow.h
24
25 # Default rules for deployment.
26 qnx: target.path = /tmp/$${TARGET}/bin
27 else: unix:!android: target.path = /opt/$${TARGET}/bin
28 !isEmpty(target.path): INSTALLS += target

In the header file "mainwindow.h", the specific code is as follows.

/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 10_udp_unicast_broadcast
* @brief mainwindow.h
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-14
*******************************************************************/
1 #ifndef MAINWINDOW_H
2 #define MAINWINDOW_H
3
4 #include <QMainWindow>
5 #include <QUdpSocket>
6 #include <QVBoxLayout>
7 #include <QHBoxLayout>
8 #include <QPushButton>
9 #include <QTextBrowser>
10 #include <QLabel>
11 #include <QComboBox>
12 #include <QSpinBox>
13 #include <QHostInfo>
14 #include <QLineEdit>
15 #include <QNetworkInterface>
16 #include <QDebug>
17
18 class MainWindow : public QMainWindow
19 {
20 Q_OBJECT
21
22 public:
23 MainWindow(QWidget *parent = nullptr);
24 ~MainWindow();
25
26 private:
27 /* Udp Communication socket */
28 QUdpSocket *udpSocket;
29
30 /* Button */
31 QPushButton *pushButton[5];
32
33 /* Label text */
34 QLabel *label[3];
35
36 /* Horizontal container */
37 QWidget *hWidget[3];
38
39 /* Horizontal layout */
40 QHBoxLayout *hBoxLayout[3];
41
42 /* Vertical container */
43 QWidget *vWidget;
44
45 /* Vertical layout */
46 QVBoxLayout *vBoxLayout;
47
48 /* Text browsing box */
49 QTextBrowser *textBrowser;
50
51 /* Used to display the local ip address */
52 QComboBox *comboBox;
53
54 /* Used to select ports */
55 QSpinBox *spinBox[2];
56
57 /* Text input box */
58 QLineEdit *lineEdit;
59
60 /* Store local ip address list */
61 QList<QHostAddress> IPlist;
62
63 /* Get all local ip addresses */
64 void getLocalHostIP();
65
66 private slots:
67 /* Binding port */
68 void bindPort();
69
70 /* Unbound port */
71 void unbindPort();
72
73 /* Content when text box is cleared */
74 void clearTextBrowser();
75
76 /* Message received */
77 void receiveMessages();
78
79 /* send message */
80 void sendMessages();
81
82 /* Broadcast message */
83 void sendBroadcastMessages();
84
85 /* Connection state change slot function */
86 void socketStateChange(QAbstractSocket::SocketState);
87 };
88 #endif // MAINWINDOW_H

The header file mainly declares the elements used for the interface and some slot functions. The focus is on declaring udpSocket.

In the source file "mainwindow.cpp", the specific code is as follows.

/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 10_udp_unicast_broadcast
* @brief mainwindow.cpp
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-14
*******************************************************************/
1 #include "mainwindow.h"
2
3 MainWindow::MainWindow(QWidget *parent)
4 : QMainWindow(parent)
5 {
6 /* Set the position and size of the main form */
7 this->setGeometry(0, 0, 800, 480);
8
9 /* udp socket */
10 udpSocket = new QUdpSocket(this);
11
12 /* Bind port button */
13 pushButton[0] = new QPushButton();
14 /* Unbind port button */
15 pushButton[1] = new QPushButton();
16 /* Empty chat text button */
17 pushButton[2] = new QPushButton();
18 /* Send Message button */
19 pushButton[3] = new QPushButton();
20 /* Broadcast message button */
21 pushButton[4] = new QPushButton();
22
23 /* Horizontal layout I */
24 hBoxLayout[0] = new QHBoxLayout();
25 /* Horizontal layout II */
26 hBoxLayout[1] = new QHBoxLayout();
27 /* Horizontal layout III */
28 hBoxLayout[2] = new QHBoxLayout();
29 /* Horizontal layout IV */
30 hBoxLayout[3] = new QHBoxLayout();
31
32 /* Horizontal container I */
33 hWidget[0] = new QWidget();
34 /* Horizontal container II */
35 hWidget[1] = new QWidget();
36 /* Horizontal container III */
37 hWidget[2] = new QWidget();
38
39
40 vWidget = new QWidget();
41 vBoxLayout = new QVBoxLayout();
42
43 /* Tag instantiation */
44 label[0] = new QLabel();
45 label[1] = new QLabel();
46 label[2] = new QLabel();
47
48 lineEdit = new QLineEdit();
49 comboBox = new QComboBox();
50 spinBox[0] = new QSpinBox();
51 spinBox[1] = new QSpinBox();
52 textBrowser = new QTextBrowser();
53
54 label[0]->setText("target IP Address:");
55 label[1]->setText("Destination port:");
56 label[2]->setText("Binding port:");
57
58 /* Sets the label size to adapt to the text size */
59 label[0]->setSizePolicy(QSizePolicy::Fixed,
60 QSizePolicy::Fixed);
61 label[1]->setSizePolicy(QSizePolicy::Fixed,
62 QSizePolicy::Fixed);
63 label[2]->setSizePolicy(QSizePolicy::Fixed,
64 QSizePolicy::Fixed);
65
66 /* Set the range of port number. Be careful not to conflict with the used port number of the host */
67 spinBox[0]->setRange(10000, 99999);
68 spinBox[1]->setRange(10000, 99999);
69
70 pushButton[0]->setText("Binding port");
71 pushButton[1]->setText("Unbind");
72 pushButton[2]->setText("Empty text");
73 pushButton[3]->setText("send message");
74 pushButton[4]->setText("Broadcast message");
75
76 /* Set stop listening status not available */
77 pushButton[1]->setEnabled(false);
78
79 /* Sets the default text for the input box */
80 lineEdit->setText("Hello!");
81
82 /* Horizontal layout - add content */
83 hBoxLayout[0]->addWidget(pushButton[0]);
84 hBoxLayout[0]->addWidget(pushButton[1]);
85 hBoxLayout[0]->addWidget(pushButton[2]);
86
87 /* Set the layout of the horizontal container to horizontal layout I */
88 hWidget[0]->setLayout(hBoxLayout[0]);
89
90 hBoxLayout[1]->addWidget(label[0]);
91 hBoxLayout[1]->addWidget(comboBox);
92 hBoxLayout[1]->addWidget(label[1]);
93 hBoxLayout[1]->addWidget(spinBox[0]);
94 hBoxLayout[1]->addWidget(label[2]);
95 hBoxLayout[1]->addWidget(spinBox[1]);
96
97 /* Set the layout of the horizontal container to horizontal layout II */
98 hWidget[1]->setLayout(hBoxLayout[1]);
99
100 /* Add content to horizontal layout III */
101 hBoxLayout[2]->addWidget(lineEdit);
102 hBoxLayout[2]->addWidget(pushButton[3]);
103 hBoxLayout[2]->addWidget(pushButton[4]);
104
105 /* Set the layout of horizontal container 3 as horizontal layout 1 */
106 hWidget[2]->setLayout(hBoxLayout[2]);
107
108 /* Add content to vertical layout */
109 vBoxLayout->addWidget(textBrowser);
110 vBoxLayout->addWidget(hWidget[1]);
111 vBoxLayout->addWidget(hWidget[0]);
112 vBoxLayout->addWidget(hWidget[2]);
113
114 /* Set the layout of the vertical container to vertical layout */
115 vWidget->setLayout(vBoxLayout);
116
117 /* Center display */
118 setCentralWidget(vWidget);
119
120 /* Get local ip */
121 getLocalHostIP();
122
123 /* Signal slot connection */
124 connect(pushButton[0], SIGNAL(clicked()),
125 this, SLOT(bindPort()));
126 connect(pushButton[1], SIGNAL(clicked()),
127 this, SLOT(unbindPort()));
128 connect(pushButton[2], SIGNAL(clicked()),
129 this, SLOT(clearTextBrowser()));
130 connect(pushButton[3], SIGNAL(clicked()),
131 this, SLOT(sendMessages()));
132 connect(pushButton[4], SIGNAL(clicked()),
133 this, SLOT(sendBroadcastMessages()));
134 connect(udpSocket, SIGNAL(readyRead()),
135 this, SLOT(receiveMessages()));
136 connect(udpSocket,
137 SIGNAL(stateChanged(QAbstractSocket::SocketState)),
138 this,
139 SLOT(socketStateChange(QAbstractSocket::SocketState)));
140 }
141
142 MainWindow::~MainWindow()
143 {
144 }
145
146 void MainWindow::bindPort()
147 {
148 quint16 port = spinBox[0]->value();
149
150 /* The binding port needs to be in the state of unconnected state in the socket */
151 if (udpSocket->state() != QAbstractSocket::UnconnectedState)
152 udpSocket->close();
153
154 if (udpSocket->bind(port)) {
155 textBrowser->append("Successfully bound port:"
156 + QString::number(port));
157
158 /* Sets the available status of elements in the interface */
159 pushButton[0]->setEnabled(false);
160 pushButton[1]->setEnabled(true);
161 spinBox[1]->setEnabled(false);
162 }
163 }
164
165 void MainWindow::unbindPort()
166 {
167 /* Unbind and stop listening */
168 udpSocket->abort();
169
170 /* Sets the available status of elements in the interface */
171 pushButton[0]->setEnabled(true);
172 pushButton[1]->setEnabled(false);
173 spinBox[1]->setEnabled(true);
174 }
175
176 /* Get local IP */
177 void MainWindow::getLocalHostIP()
178 {
179 // /*Gets the name of the host*/
180 // QString hostName = QHostInfo::localHostName();
181
182 // /*Host information*/
183 // QHostInfo hostInfo = QHostInfo::fromName(hostName);
184
185 // /*ip list. addresses returns the ip address list. Note that the host should be able to obtain it from the router
186 // *IP, otherwise an empty list may be returned (ubuntu can only obtain loopback IP with this method)*/
187 // IPlist = hostInfo.addresses();
188 // qDebug()<<IPlist<<endl;
189
190 // /*Traverse IPlist*/
191 // foreach (QHostAddress ip, IPlist) {
192 // if (ip.protocol() == QAbstractSocket::IPv4Protocol)
193 // comboBox->addItem(ip.toString());
194 // }
195
196 /* Get all network interfaces,
197 * QNetworkInterface Class provides a list of host IP addresses and network interfaces */
198 QList<QNetworkInterface> list
199 = QNetworkInterface::allInterfaces();
200
201 /* Traversal list */
202 foreach (QNetworkInterface interface, list) {
203
204 /* QNetworkAddressEntry Class stores IP addresses, subnet masks, and broadcast addresses */
205 QList<QNetworkAddressEntry> entryList
206 = interface.addressEntries();
207
208 /* Traverse entryList */
209 foreach (QNetworkAddressEntry entry, entryList) {
210 /* Filter IPv6 addresses, leaving only IPv4 */
211 if (entry.ip().protocol() ==
212 QAbstractSocket::IPv4Protocol) {
213 comboBox->addItem(entry.ip().toString());
214 /* Add to IP list */
215 IPlist<<entry.ip();
216 }
217 }
218 }
219 }
220
221 /* Clear the contents of the text browsing box */
222 void MainWindow::clearTextBrowser()
223 {
224 /* Clear the contents of the text browser */
225 textBrowser->clear();
226 }
227
228 /* Client receives message */
229 void MainWindow::receiveMessages()
230 {
231 /* Local variable, which is used to obtain the IP and port of the sender */
232 QHostAddress peerAddr;
233 quint16 peerPort;
234
235 /* If data is ready */
236 while (udpSocket->hasPendingDatagrams()) {
237 /* udpSocket The datagram sent is a byte array of type QByteArray */
238 QByteArray datagram;
239
240 /* Redefine the size of the array */
241 datagram.resize(udpSocket->pendingDatagramSize());
242
243 /* Read the data and obtain the IP address and port of the sender */
244 udpSocket->readDatagram(datagram.data(),
245 datagram.size(),
246 &peerAddr,
247 &peerPort);
248 /* Convert to string */
249 QString str = datagram.data();
250
251 /* Displays information to the text browsing box window */
252 textBrowser->append("Receive from"
253 + peerAddr.toString()
254 + ":"
255 + QString::number(peerPort)
256 + str);
257 }
258 }
259
260 /* Client send message */
261 void MainWindow::sendMessages()
262 {
263 /* The text browsing box displays the sent information */
264 textBrowser->append("send out:" + lineEdit->text());
265
266 /* The information to be sent is converted to QByteArray type byte array, and the data is generally less than 512 bytes */
267 QByteArray data = lineEdit->text().toUtf8();
268
269 /* Destination Ip address to send */
270 QHostAddress peerAddr = IPlist[comboBox->currentIndex()];
271
272 /* Destination port number to send */
273 quint16 peerPort = spinBox[1]->value();
274
275 /* send message */
276 udpSocket->writeDatagram(data, peerAddr, peerPort);
277 }
278
279 void MainWindow::sendBroadcastMessages()
280 {
281 /* The text browsing box displays the sent information */
282 textBrowser->append("send out:" + lineEdit->text());
283
284 /* The information to be sent is converted to QByteArray type byte array, and the data is generally less than 512 bytes */
285 QByteArray data = lineEdit->text().toUtf8();
286
287 /* Broadcast address, generally 255.255 255.255,
288 * Programs listening to the target port in the same network segment will receive messages */
289 QHostAddress peerAddr = QHostAddress::Broadcast;
290
291 /* Destination port number to send */
292 quint16 peerPort = spinBox[1]->text().toInt();
293
294 /* send message */
295 udpSocket->writeDatagram(data, peerAddr, peerPort);
296 }
297 /* socket State change */
298 void MainWindow::socketStateChange(QAbstractSocket::SocketState
state)
299 {
300 switch (state) {
301 case QAbstractSocket::UnconnectedState:
302 textBrowser->append("scoket Status: UnconnectedState");
303 break;
304 case QAbstractSocket::ConnectedState:
305 textBrowser->append("scoket Status: ConnectedState");
306 break;
307 case QAbstractSocket::ConnectingState:
308 textBrowser->append("scoket Status: ConnectingState");
309 break;
310 case QAbstractSocket::HostLookupState:
311 textBrowser->append("scoket Status: HostLookupState");
312 break;
313 case QAbstractSocket::ClosingState:
314 textBrowser->append("scoket Status: ClosingState");
315 break;
316 case QAbstractSocket::ListeningState:
317 textBrowser->append("scoket Status: ListeningState");
318 break;
319 case QAbstractSocket::BoundState:
320 textBrowser->append("scoket Status: BoundState");
321 break;
322 default:
323 break;
324 }
325 }

Lines 146 to 163, bind ports. Use the bind method to bind a port. Note that the port we bind cannot conflict with the port already used by the host!
Lines 165-174, unbind the port. Use the abort method to unbind.
Lines 229-258: receive the message. Note that the received message is a QByteArray byte array. The readDatagram method is used to read the array. In the readDatagram method, you can obtain the socket IP address and port number of the other party.
Lines 261 ~ 277, unicast message, need to know the target IP and target port number. The writeDatagram method can be used to send messages.
In lines 279 ~ 296, the difference between the broadcast message and the unicast message is that the target IP address is replaced by the broadcast address. The general broadcast address is 255.255 255.255.

2. Program running effect

This example can be used as both sender and receiver. If you run two programs in the same system on the same host. Cannot bind the same port! Otherwise it will conflict! When you want to test running this program on different hosts in the same LAN, the bound port number can be the same.

This example sets the target IP address to 127.0 0.1, which is the loopback IP address on Ubuntu/Windows and can be used for testing without network. The binding port number is the same as the target port number, that is, the program is listening for data with port number 10000, and the program is also sending data to the target IP address 127.0 The 10000 port number of 0.1 sends data. In fact, this program completes spontaneous self collection.

When we click the send message button, the text message window displays the sent data "Hello!", At the same time, received by the local IP 127.0 The data sent by 0.1 is "Hello!". Where ffff: is the identification of the communication socket. ha-ha! You may ask why it is not another address of this host, such as (192.168.1.x)? Because we have selected the IP address of the target as 127.0.0.1, we must use IP devices of the same network segment to communicate with this target address. Note that messages cannot be sent to other hosts with local loopback. Because local loopback IP is only applicable to IP communication on the local host.

When we click the broadcast message button, the target IP address sent by the broadcast becomes the broadcast address 255.255 255.255. Then we will receive 192.168 from the local IP address x. X data. As shown in the figure below, received from 192.168 1.129 data sent. Because loopback IP 127.0 The broadcast address of 0.1 is 255.0 0.0, so it should be consistent with 255.255 The IP communication data in the network segment of 255.255 must be from 192.168.255 x. From X. If other hosts on the same network segment are listening on the target port, they will receive messages at the same time. This also verifies why the previous section changed from 127.0 0.1 send data.

This example is not difficult. It may be a little windy. Please refer to more materials for understanding. There are a lot of knowledge points. If there is no communication foundation, we need to understand it slowly.

3. UDP multicast

Generally, in the traditional network communication, there are two ways, one is the "one-to-one" communication between the source host and the target host, that is, unicast, and the other is the communication between a source host and all other hosts in the network, that is, broadcast. Then, if the information needs to be sent from the source host to multiple target hosts in the network, either broadcast is adopted, so that all hosts in the network will receive the information, or unicast is adopted, and the source host sends the information to different target hosts respectively. It can be seen that in the broadcast mode, the information will be sent to the host that does not need the information, thus wasting bandwidth resources and even causing a broadcast storm. In the unicast mode, bandwidth resources will be wasted due to multiple repetitions of data packets. At the same time, the load of the source host will increase due to multiple data replication. Therefore, unicast and broadcast have defects for multicast. In this case, multicast technology is applied.

Multicast is similar to QQ group. If Tencent sends push messages to each QQ user as broadcast, multicast is like QQ group. Only users in the group can receive messages. If we want to receive a message, we have to add a group first.

The first byte of a class D IP address must start with "1110". Class D IP address is a specially reserved address, regardless of network address and host address, and its address range is 224.0 0.0~239.255. 255.255. Class D IP address is mainly used as multicast group IP address in multicast (multicast). Multicast group IP address enables the source host to send packets to a group of hosts in the network, and hosts belonging to multicast group will be assigned a multicast group lP address.

Since the multicast group lP address identifies a group of hosts (also known as host group), the multicast group IP address can only be used as the destination address, and the source address is always a unicast address.

 224.0. 0.0~224.0. 0.255 is the reserved multicast address (permanent group address), address 224.0.0.0 is reserved for allocation, and other addresses are used by the routing protocol.
 224.0. 1.0~238.255. 255.255 is the multicast address (temporary group address) available to users, which is valid in the whole network.
 239.0. 0.0~239.255. 255.255 is the local management multicast address,

From the above information, we only need to pay attention to which multicast addresses can be used by our local host. If UDP multicast function is used in home network and office network LAN, the available multicast address range is 239.0 0.0~ ~239.255. 255.255.

QUdpSocket class supports UDP multicast and provides the joinMulticastGroup method to enable the local host to join the multicast group,
Leavemulticast group leaves the multicast group. For other binding ports, the sending and receiving functions are exactly the same as UDP unicast and broadcast. In fact, we learned to use the applications of joinMulticastGroup and leaveMulticastGroup in the previous example!

1. Application examples

Purpose of this example: to understand the use of QUdpSocket multicast.

Example 11_ udp_ multicast, UDP unicast and broadcast applications (difficulty: average). The project path is Qt/2/11_udp_multicast. The general process of this example first obtains the local IP address. Create a UDP socket and bind the port of the local host before joining multicast. Join multicast using joinMulticastGroup and exit multicast using leaveMulticastGroup. Other messaging functions are the same as unicast and broadcast in the previous section.

Project document 10_ udp_ unicast_ broadcast. The code added to the first line of the pro file is as follows.

1 QT += core gui network
2
3 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
4
5 CONFIG += c++11
6
7 # The following define makes your compiler emit warnings if you use
8 # any Qt feature that has been marked deprecated (the exact warnings
9 # depend on your compiler). Please consult the documentation of the
10 # deprecated API in order to know how to port your code away from it.
11 DEFINES += QT_DEPRECATED_WARNINGS
12
13 # You can also make your code fail to compile if it uses deprecated APIs.
14 # In order to do so, uncomment the following line.
15 # You can also select to disable deprecated APIs only up to a certain
version of Qt.
16 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the
APIs deprecated before Qt 6.0.0
17
18 SOURCES += \
19 main.cpp \
20 mainwindow.cpp
21
22 HEADERS += \
23 mainwindow.h
24
25 # Default rules for deployment.
26 qnx: target.path = /tmp/$${TARGET}/bin
27 else: unix:!android: target.path = /opt/$${TARGET}/bin
28 !isEmpty(target.path): INSTALLS += target

In the header file "mainwindow.h", the specific code is as follows.

/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 10_udp_unicast_broadcast
* @brief mainwindow.h
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-14
*******************************************************************/
1 #ifndef MAINWINDOW_H
2 #define MAINWINDOW_H
3
4 #include <QMainWindow>
5 #include <QUdpSocket>
6 #include <QVBoxLayout>
7 #include <QHBoxLayout>
8 #include <QPushButton>
9 #include <QTextBrowser>
10 #include <QLabel>
11 #include <QComboBox>
12 #include <QSpinBox>
13 #include <QHostInfo>
14 #include <QLineEdit>
15 #include <QNetworkInterface>
16 #include <QDebug>
17
18 class MainWindow : public QMainWindow
19 {
20 Q_OBJECT
21
22 public:
23 MainWindow(QWidget *parent = nullptr);
24 ~MainWindow();
25
26 private:
27 /* Udp Communication socket */
28 QUdpSocket *udpSocket;
29
30 /* Button */
31 QPushButton *pushButton[4];
32
33 /* Label text */
34 QLabel *label[3];
35
36 /* Horizontal container */
37 QWidget *hWidget[3];
38
39 /* Horizontal layout */
40 QHBoxLayout *hBoxLayout[3];
41
42 /* Vertical container */
43 QWidget *vWidget;
44
45 /* Vertical layout */
46 QVBoxLayout *vBoxLayout;
47
48 /* Text browsing box */
49 QTextBrowser *textBrowser;
50
51 /* Used to display the local ip address */
52 QComboBox *comboBox[2];
53
54 /* Used to select ports */
55 QSpinBox *spinBox;
56
57 /* Text input box */
58 QLineEdit *lineEdit;
59
60 /* Store local ip address list */
61 QList<QHostAddress> IPlist;
62
63 /* Get all local ip addresses */
64 void getLocalHostIP();
65
66 private slots:
67 /* Join multicast */
68 void joinGroup();
69
70 /* Exit multicast */
71 void leaveGroup();
72
73 /* Content when text box is cleared */
74 void clearTextBrowser();
75
76 /* Message received */
77 void receiveMessages();
78
79 /* Multicast message */
80 void sendMessages();
81
82 /* Connection state change slot function */
83 void socketStateChange(QAbstractSocket::SocketState);
84 };
85 #endif // MAINWINDOW_H

The header file mainly declares the elements used for the interface and some slot functions. The focus is on declaring udpSocket.

In the source file "mainwindow.cpp", the specific code is as follows.

/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 10_udp_unicast_broadcast
* @brief mainwindow.cpp
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-14
*******************************************************************/
1 #include "mainwindow.h"
2
3 MainWindow::MainWindow(QWidget *parent)
4 : QMainWindow(parent)
5 {
6 /* Set the position and size of the main form */
7 this->setGeometry(0, 0, 800, 480);
8
9 /* udp socket */
10 udpSocket = new QUdpSocket(this);
11
12 /* Parameter 1 is to set the IP address_ MULTICAST_ The TTL socket option allows applications to primarily restrict packets in
Internet Survival time in,
13 * And prevent it from looping indefinitely. Datagrams will be reduced by one across one route. The default value is 1, indicating that multicast is only applicable to
 Local subnet.*/
14 udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,
1);
15
16 /* Join multicast button */
17 pushButton[0] = new QPushButton();
18 /* Exit multicast button */
19 pushButton[1] = new QPushButton();
20 /* Empty chat text button */
21 pushButton[2] = new QPushButton();
22 /* Multicast message button */
23 pushButton[3] = new QPushButton();
24
25 /* Horizontal layout I */
26 hBoxLayout[0] = new QHBoxLayout();
27 /* Horizontal layout II */
28 hBoxLayout[1] = new QHBoxLayout();
29 /* Horizontal layout III */
30 hBoxLayout[2] = new QHBoxLayout();
31 /* Horizontal layout IV */
32 hBoxLayout[3] = new QHBoxLayout();
33
34 /* Horizontal container I */
35 hWidget[0] = new QWidget();
36 /* Horizontal container II */
37 hWidget[1] = new QWidget();
38 /* Horizontal container III */
39 hWidget[2] = new QWidget();
40
41
42 vWidget = new QWidget();
43 vBoxLayout = new QVBoxLayout();
44
45 /* Tag instantiation */
46 label[0] = new QLabel();
47 label[1] = new QLabel();
48 label[2] = new QLabel();
49
50 lineEdit = new QLineEdit();
51 comboBox[0] = new QComboBox();
52 comboBox[1] = new QComboBox();
53 spinBox = new QSpinBox();
54 textBrowser = new QTextBrowser();
55
56 label[0]->setText("local IP Address:");
57 label[1]->setText("Multicast address:");
58 label[2]->setText("Multicast port:");
59
60 /* Sets the label size to adapt to the text size */
61 label[0]->setSizePolicy(QSizePolicy::Fixed,
62 QSizePolicy::Fixed);
63 label[1]->setSizePolicy(QSizePolicy::Fixed,
64 QSizePolicy::Fixed);
65 label[2]->setSizePolicy(QSizePolicy::Fixed,
66 QSizePolicy::Fixed);
67
68 /* Set the range of port number. Be careful not to conflict with the used port number of the host */
69 spinBox->setRange(10000, 99999);
70
71 pushButton[0]->setText("Join multicast");
72 pushButton[1]->setText("Exit multicast");
73 pushButton[2]->setText("Empty text");
74 pushButton[3]->setText("Multicast message");
75
76 /* Set stop listening status not available */
77 pushButton[1]->setEnabled(false);
78
79 /* Sets the default text for the input box */
80 lineEdit->setText("Hello!");
81
82 /* Add a multicast address in the range by default */
83 comboBox[1]->addItem("239.255.255.1");
84
85 /* The setting can be edited, and the user can modify this address by himself */
86 comboBox[1]->setEditable(true);
87
88 /* Horizontal layout - add content */
89 hBoxLayout[0]->addWidget(pushButton[0]);
90 hBoxLayout[0]->addWidget(pushButton[1]);
91 hBoxLayout[0]->addWidget(pushButton[2]);
92
93 /* Set the layout of the horizontal container to horizontal layout I */
94 hWidget[0]->setLayout(hBoxLayout[0]);
95
96 hBoxLayout[1]->addWidget(label[0]);
97 hBoxLayout[1]->addWidget(comboBox[0]);
98 hBoxLayout[1]->addWidget(label[1]);
99 hBoxLayout[1]->addWidget(comboBox[1]);
100 hBoxLayout[1]->addWidget(label[2]);
101 hBoxLayout[1]->addWidget(spinBox);
102
103 /* Set the layout of the horizontal container to horizontal layout II */
104 hWidget[1]->setLayout(hBoxLayout[1]);
105
106 /* Add content to horizontal layout III */
107 hBoxLayout[2]->addWidget(lineEdit);
108 hBoxLayout[2]->addWidget(pushButton[3]);
109
110 /* Set the layout of horizontal container 3 as horizontal layout 1 */
111 hWidget[2]->setLayout(hBoxLayout[2]);
112
113 /* Add content to vertical layout */
114 vBoxLayout->addWidget(textBrowser);
115 vBoxLayout->addWidget(hWidget[1]);
116 vBoxLayout->addWidget(hWidget[0]);
117 vBoxLayout->addWidget(hWidget[2]);
118
119 /* Set the layout of the vertical container to vertical layout */
120 vWidget->setLayout(vBoxLayout);
121
122 /* Center display */
123 setCentralWidget(vWidget);
124
125 /* Get local ip */
126 getLocalHostIP();
127
128 /* Signal slot connection */
129 connect(pushButton[0], SIGNAL(clicked()),
130 this, SLOT(joinGroup()));
131 connect(pushButton[1], SIGNAL(clicked()),
132 this, SLOT(leaveGroup()));
133 connect(pushButton[2], SIGNAL(clicked()),
134 this, SLOT(clearTextBrowser()));
135 connect(pushButton[3], SIGNAL(clicked()),
136 this, SLOT(sendMessages()));
137 connect(udpSocket, SIGNAL(readyRead()),
138 this, SLOT(receiveMessages()));
139 connect(udpSocket,
140 SIGNAL(stateChanged(QAbstractSocket::SocketState)),
141 this,
142 SLOT(socketStateChange(QAbstractSocket::SocketState)));
143 }
144
145 MainWindow::~MainWindow()
146 {
147 }
148
149 void MainWindow::joinGroup()
150 {
151 /* Get port */
152 quint16 port = spinBox->value();
153 /* Get multicast address */
154 QHostAddress groupAddr = QHostAddress(comboBox[1]->currentText());
155
156 /* The binding port needs to be in the state of unconnected state in the socket */
157 if (udpSocket->state() != QAbstractSocket::UnconnectedState)
158 udpSocket->close();
159
160 /* The port must be bound before joining multicast */
161 if (udpSocket->bind(QHostAddress::AnyIPv4,
162 port, QUdpSocket::ShareAddress)) {
163
164 /* Join the multicast group and return the result to the ok variable */
165 bool ok = udpSocket->joinMulticastGroup(groupAddr);
166
167 textBrowser->append(ok ? "Join multicast successfully" : "Failed to join multicast");
168
169 textBrowser->append("Multicast address IP:"
170 + comboBox[1]->currentText());
171
172 textBrowser->append("Binding port:"
173 + QString::number(port));
174
175 /* Sets the available status of elements in the interface */
176 pushButton[0]->setEnabled(false);
177 pushButton[1]->setEnabled(true);
178 comboBox[1]->setEnabled(false);
179 spinBox->setEnabled(false);
180 }
181 }
182
183 void MainWindow::leaveGroup()
184 {
185 /* Get multicast address */
186 QHostAddress groupAddr = QHostAddress(comboBox[1]->currentText());
187
188 /* Exit multicast */
189 udpSocket->leaveMulticastGroup(groupAddr);
190
191 /* Unbind and stop listening */
192 udpSocket->abort();
193
194 /* Sets the available status of elements in the interface */
195 pushButton[0]->setEnabled(true);
196 pushButton[1]->setEnabled(false);
197 comboBox[1]->setEnabled(true);
198 spinBox->setEnabled(true);
199 }
200
201 /* Get local IP */
202 void MainWindow::getLocalHostIP()
203 {
204 // /*Gets the name of the host*/
205 // QString hostName = QHostInfo::localHostName();
206
207 // /*Host information*/
208 // QHostInfo hostInfo = QHostInfo::fromName(hostName);
209
210 // /*ip list. addresses returns the ip address list. Note that the host should be able to obtain it from the router
211 // *IP, otherwise an empty list may be returned (ubuntu can only obtain loopback IP with this method)*/
212 // IPlist = hostInfo.addresses();
213 // qDebug()<<IPlist<<endl;
214
215 // /*Traverse IPlist*/
216 // foreach (QHostAddress ip, IPlist) {
217 // if (ip.protocol() == QAbstractSocket::IPv4Protocol)
218 // comboBox->addItem(ip.toString());
219 // }
220
221 /* Get all network interfaces,
222 * QNetworkInterface Class provides a list of host IP addresses and network interfaces */
223 QList<QNetworkInterface> list
224 = QNetworkInterface::allInterfaces();
225
226 /* Traversal list */
227 foreach (QNetworkInterface interface, list) {
228
229 /* QNetworkAddressEntry Class stores IP addresses, subnet masks, and broadcast addresses */
230 QList<QNetworkAddressEntry> entryList
231 = interface.addressEntries();
232
233 /* Traverse entryList */
234 foreach (QNetworkAddressEntry entry, entryList) {
235 /* Filter IPv6 addresses, leaving only IPv4 and no loopback IP */
236 if (entry.ip().protocol() ==
237 QAbstractSocket::IPv4Protocol &&
238 ! entry.ip().isLoopback()) {
239 /* Add local IP address to comboBox[0] */
240 comboBox[0]->addItem(entry.ip().toString());
241 /* Add to IP list */
242 IPlist<<entry.ip();
243 }
244 }
245 }
246 }
247
248 /* Clear the contents of the text browsing box */
249 void MainWindow::clearTextBrowser()
250 {
251 /* Clear the contents of the text browser */
252 textBrowser->clear();
253 }
254
255 /* Client receives message */
256 void MainWindow::receiveMessages()
257 {
258 /* Local variable, which is used to obtain the IP and port of the sender */
259 QHostAddress peerAddr;
260 quint16 peerPort;
261
262 /* If data is ready */
263 while (udpSocket->hasPendingDatagrams()) {
264 /* udpSocket The datagram sent is a byte array of type QByteArray */
265 QByteArray datagram;
266
267 /* Redefine the size of the array */
268 datagram.resize(udpSocket->pendingDatagramSize());
269
270 /* Read the data and obtain the IP address and port of the sender */
271 udpSocket->readDatagram(datagram.data(),
272 datagram.size(),
273 &peerAddr,
274 &peerPort);
275 /* Convert to string */
276 QString str = datagram.data();
277
278 /* Displays information to the text browsing box window */
279 textBrowser->append("Receive from"
280 + peerAddr.toString()
281 + ":"
282 + QString::number(peerPort)
283 + str);
284 }
285 }
286
287 /* Client send message */
288 void MainWindow::sendMessages()
289 {
290 /* The text browsing box displays the sent information */
291 textBrowser->append("send out:" + lineEdit->text());
292
293 /* The information to be sent is converted to QByteArray type byte array, and the data is generally less than 512 bytes */
294 QByteArray data = lineEdit->text().toUtf8();
295
296 /* Destination Ip address to send */
297 QHostAddress groupAddr = QHostAddress(comboBox[1]->currentText());
298
299 /* Destination port number to send */
300 quint16 groupPort = spinBox->value();
301
302 /* send message */
303 udpSocket->writeDatagram(data, groupAddr, groupPort);
304 }
305
306 /* socket State change */
307 void MainWindow::socketStateChange(QAbstractSocket::SocketState
state)
308 {
309 switch (state) {
310 case QAbstractSocket::UnconnectedState:
311 textBrowser->append("scoket Status: UnconnectedState");
312 break;
313 case QAbstractSocket::ConnectedState:
314 textBrowser->append("scoket Status: ConnectedState");
315 break;
316 case QAbstractSocket::ConnectingState:
317 textBrowser->append("scoket Status: ConnectingState");
318 break;
319 case QAbstractSocket::HostLookupState:
320 textBrowser->append("scoket Status: HostLookupState");
321 break;
322 case QAbstractSocket::ClosingState:
323 textBrowser->append("scoket Status: ClosingState");
324 break;
325 case QAbstractSocket::ListeningState:
326 textBrowser->append("scoket Status: ListeningState");
327 break;
328 case QAbstractSocket::BoundState:
329 textBrowser->append("scoket Status: BoundState");
330 break;
331 default:
332 break;
333 }
334 }

Lines 161 to 162, bind ports. Use the bind method to bind a port. Note that the port we bind cannot conflict with the port already used by the host!
In line 165, join multicast using joinmulticast group. QHostAddress::AnyIPv4 is an interface for joining Ipv4 multicast. All operating systems do not support joining IPv6 Multicast group without interface selection. The added result is returned to the variable ok. The multicast address can be entered by the user by clicking the comboBox[1] control (the default editor has entered an address of 239.255.255.1). Note that the multicast address range must be a number from 239.0.0.0 to 239.255.255.255.
Line 189, use leavemulticast group to exit multicast.
Line 192, unbind the port. Use the abort method to unbind.
Lines 256-285: receive the message. Note that the received message is a QByteArray byte array. The readDatagram method is used to read the array. In the readDatagram method, you can obtain the socket IP address and port number of the other party.
Lines 288 ~ 304, send the message. The difference between multicast and broadcast message or unicast message is that the target IP address is replaced by multicast address 239.255 255.1.

2. Program running effect

After running the program, click join multicast, and then click multicast message. This instance can be both the sender and the receiver. If you run two programs in the same system on the same host. Cannot bind the same port! Otherwise it will conflict! When you want to test running this program on different hosts in the same LAN, the bound port number can be the same.

Because it is a multicast message, you will also receive messages. If you run this program on other hosts in the LAN, you can send and receive messages after clicking Join multicast.

4, Network download instance

The Qt network module also provides classes for direct access to network protocols such as HTTP and FTP. These classes are QNetworkAccessManager, QNetworkRequest and QNetworkReply.

These three classes usually need to cooperate to complete a network operation. It can be used to obtain time, weather, pictures and other data from the network. For example, this example needs to download an image. The general process is as follows.

The QNetworkRequest class sets a URL address to initiate a network protocol request, and the QNetworkRequest class saves the request to be sent with the QNetworkAccessManager. QNetworkRequest is a part of the network access API. It is a class that holds the information required to send requests through the network. It contains a URL and some auxiliary information that can be used to modify the request.

The QNetworkAccessManager class allows applications to send network requests and receive responses. After the QNetworkRequest initiates the network request, the qnetworkaccess manager is responsible for sending the network request and creating the network response.

The QNetworkReply class is used for the network response created by QNetworkAccessManager. Finally, QNetworkReply processes the network response. It provides signals such as finished(), readyRead() and downloadProgress(), which can monitor the execution of network response. And QNetworkReply inherits from QIODevice, so QNetworkReply supports stream reading and writing, and can directly use read() and write functions.

1. Application examples

Purpose of this example: understand the use of QNetworkAccessManager, QNetworkRequest and QNetworkReply classes.

Example 12_ Imagedownload: Download Small pictures (difficulty: average). The project path is Qt/2/12_imagedownload. This example is a general process. Set a URL for downloading pictures, process the response through networkReply, read the picture data from the stream, and then save it locally.

Project documents 12_ imagedownload. The code added to the first line of the pro file is as follows.

1 QT += core gui network
2
3 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
4
5 CONFIG += c++11
6
7 # The following define makes your compiler emit warnings if you use
8 # any Qt feature that has been marked deprecated (the exact warnings
9 # depend on your compiler). Please consult the documentation of the
10 # deprecated API in order to know how to port your code away from it.
11 DEFINES += QT_DEPRECATED_WARNINGS
12
13 # You can also make your code fail to compile if it uses deprecated APIs.
14 # In order to do so, uncomment the following line.
15 # You can also select to disable deprecated APIs only up to a certain
version of Qt.
16 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the
APIs deprecated before Qt 6.0.0
17
18 SOURCES += \
19 main.cpp \
20 mainwindow.cpp
21
22 HEADERS += \
23 mainwindow.h
24
25 # Default rules for deployment.
26 qnx: target.path = /tmp/$${TARGET}/bin
27 else: unix:!android: target.path = /opt/$${TARGET}/bin
28 !isEmpty(target.path): INSTALLS += target

In the header file "mainwindow.h", the specific code is as follows.

/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 12_imagedownload
* @brief mainwindow.h
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-16
*******************************************************************/
1 #ifndef MAINWINDOW_H
2 #define MAINWINDOW_H
3
4 #include <QMainWindow>
5 #include <QNetworkAccessManager>
6 #include <QNetworkReply>
7 #include <QFile>
8 #include <QLabel>
9 #include <QPushButton>
10 #include <QProgressBar>
11 #include <QHBoxLayout>
12 #include <QVBoxLayout>
13 #include <QLineEdit>
14
15 class MainWindow : public QMainWindow
16 {
17 Q_OBJECT
18
19 public:
20 MainWindow(QWidget *parent = nullptr);
21 ~MainWindow();
22 private:
23 /* Network management */
24 QNetworkAccessManager *networkAccessManager;
25
26 /* label */
27 QLabel *label[3];
28
29 /* Button */
30 QPushButton *pushButton;
31
32 /* Download progress bar */
33 QProgressBar *progressBar;
34
35 /* Horizontal layout */
36 QHBoxLayout *hBoxLayout[2];
37
38 /* Vertical layout */
39 QVBoxLayout *vBoxLayout;
40
41 /* Horizontal container */
42 QWidget *hWidget[2];
43
44 /* Vertical container */
45 QWidget *vWidget;
46
47 /* Link input box */
48 QLineEdit *lineEdit;
49
50 private slots:
51 /* Read data */
52 void readyReadData();
53
54 /* Response completion processing */
55 void replyFinished();
56
57 /* Download progress management */
58 void imageDownloadProgress(qint64, qint64);
59
60 /* Click start download */
61 void startDownload();
62
63 /* Response error handling function */
64 void networkReplyError(QNetworkReply::NetworkError);
65 };
66 #endif // MAINWINDOW_H

The header file mainly declares the elements used for the interface and some slot functions. The focus is on declaring the network access manager.

In the source file "mainwindow.cpp", the specific code is as follows.

/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 12_imagedownload
* * @brief mainwindow.cpp
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-16
*******************************************************************/
1 #include "mainwindow.h"
2 #include <QMessageBox>
3 #include <QCoreApplication>
4
5 MainWindow::MainWindow(QWidget *parent)
6 : QMainWindow(parent)
7 {
8 /* Set the position and size of the main form */
9 this->setGeometry(0, 0, 800, 480);
10
11 /* Tag 0, showing the downloaded image */
12 label[0] = new QLabel();
13 /* Tag 1, display URL tag */
14 label[1] = new QLabel();
15 /* Download progress tab */
16 label[2] = new QLabel();
17
18 /* Download picture link input box */
19 lineEdit = new QLineEdit();
20
21 /* Download button */
22 pushButton = new QPushButton();
23
24 /* Download progress bar */
25 progressBar = new QProgressBar();
26
27 /* Horizontal layout */
28 hBoxLayout[0] = new QHBoxLayout();
29 hBoxLayout[1] = new QHBoxLayout();
30
31 /* Vertical layout */
32 vBoxLayout = new QVBoxLayout();
33
34 /* Horizontal container */
35 hWidget[0] = new QWidget();
36 hWidget[1] = new QWidget();
37
38 /* Vertical container */
39 vWidget = new QWidget();
40
41 label[1]->setText("URL Link:");
42 label[2]->setText("File download progress:");
43
44 pushButton->setText("download");
45
46 /* Set download link address */
47 lineEdit->setText("https://ss0.bdstatic.com/70cFuH"
48 "Sh_Q1YnxGkpoWK1HF6hhy/it/u=42710"
49 "87328,1384669424&fm=11&gp=0.jpg");
50 /* Sets the minimum display size of the label */
51 label[0]->setMinimumSize(this->width(),
52 this->height() * 0.75);
53
54 /* Adaptive size based on text size */
55 label[1]->setSizePolicy(QSizePolicy::Fixed,
56 QSizePolicy::Fixed);
57 label[2]->setSizePolicy(QSizePolicy::Fixed,
58 QSizePolicy::Fixed);
59 pushButton->setSizePolicy(QSizePolicy::Fixed,
60 QSizePolicy::Fixed);
61
62 /* Add elements to horizontal layout 0 */
63 hBoxLayout[0]->addWidget(label[1]);
64 hBoxLayout[0]->addWidget(lineEdit);
65 hBoxLayout[0]->addWidget(pushButton);
66
67 /* Set the horizontal layout 0 to the layout 0 of the horizontal container */
68 hWidget[0]->setLayout(hBoxLayout[0]);
69
70 /* Add elements to horizontal layout 1 */
71 hBoxLayout[1]->addWidget(label[2]);
72 hBoxLayout[1]->addWidget(progressBar);
73
74 /* Set horizontal layout 1 to layout 1 of the horizontal container */
75 hWidget[1]->setLayout(hBoxLayout[1]);
76
77 /* Add elements to vertical layout */
78 vBoxLayout->addWidget(label[0]);
79 vBoxLayout->addWidget(hWidget[0]);
80 vBoxLayout->addWidget(hWidget[1]);
81
82 /* Set the vertical layout to the layout of the vertical container */
83 vWidget->setLayout(vBoxLayout);
84
85 /* Set center */
86 setCentralWidget(vWidget);
87
88 /* Network management */
89 networkAccessManager = new QNetworkAccessManager(this);
90
91 /* Signal slot connection */
92 connect(pushButton, SIGNAL(clicked()),
93 this, SLOT(startDownload()));
94
95 }
96
97 MainWindow::~MainWindow()
98 {
99 }
100
101 void MainWindow::startDownload()
102 {
103 /* Get URL link */
104 QUrl newUrl(QUrl(lineEdit->text()));
105
106 /* If the download link is invalid, return directly */
107 if (!newUrl.isValid()) {
108 QMessageBox::information(this, "error", "invalid url");
109 return;
110 }
111
112 /* Network request */
113 QNetworkRequest networkRequest;
114
115 /* Set download address */
116 networkRequest.setUrl(newUrl);
117
118 /* Network response */
119 QNetworkReply *newReply =
120 networkAccessManager->get(networkRequest);
121
122 /* Signal slot connection */
123 connect(newReply, SIGNAL(finished()),
124 this, SLOT(replyFinished()));
125 connect(newReply, SIGNAL(readyRead()),
126 this, SLOT(readyReadData()));
127 connect(newReply, SIGNAL(downloadProgress(qint64, qint64)),
128 this, SLOT(imageDownloadProgress(qint64, qint64)));
129 connect(newReply,
130 SIGNAL(error(QNetworkReply::NetworkError)),
131 this,
132 SLOT(networkReplyError(QNetworkReply::NetworkError )));
133 }
134
135 void MainWindow::readyReadData()
136 {
137 /* The setting button is unavailable to prevent unfinished, click again */
138 pushButton->setEnabled(false);
139
140 /* Get signal sender */
141 QNetworkReply *reply = (QNetworkReply *)sender();
142
143 QFile imageFile;
144 /* Save to the current path with the name "downloaded. jpg" */
145 imageFile.setFileName(QCoreApplication::applicationDirPath()
146 + "/Downloaded.jpg");
147
148 /* If this picture already exists, delete it */
149 if (imageFile.exists())
150 imageFile.remove();
151
152 /* Read data */
153 QByteArray data = reply->readAll();
154 /* If the data is empty, return */
155 if (data.isEmpty()) {
156 qDebug()<<"data is null, please try it again!"<<endl;
157 return;
158 }
159
160 /* Judge whether it is a picture in JPG format. If not, return */
161 if (! (data[0] == (char)0xff
162 && data[1] == (char)0xd8
163 && data[data.size() - 2] == (char)0xff
164 && data[data.size() - 1] == (char)0xd9)) {
165 qDebug()<<"not JPG data, please try it again!"<<endl;
166 return;
167 }
168
169 /* Convert to QPixmap */
170 QPixmap pixmap;
171 pixmap.loadFromData(data);
172 pixmap.save(imageFile.fileName());
173 }
174
175 void MainWindow::replyFinished()
176 {
177 /* Get signal sender */
178 QNetworkReply *reply = (QNetworkReply *)sender();
179
180 /* Prevent memory leaks */
181 reply->deleteLater();
182
183 /* Judge whether the image under the current execution program is downloaded */
184 QFile imageFile(QCoreApplication::applicationDirPath()
185 + "/Downloaded.jpg");
186 if (imageFile.exists()) {
187 /* Displays the downloaded image */
188 label[0]->setPixmap(QPixmap(imageFile.fileName()));
189 qDebug() <<"Successfully downloaded with file path:"
190 <<imageFile.fileName()<<endl;
191 } else
192 /* Clear display */
193 label[0]->clear();
194
195 /* The settings button is available */
196 pushButton->setEnabled(true);
197 }
198
199 void MainWindow::imageDownloadProgress(qint64 bytes,
200 qint64 totalBytes)
201 {
202 /* Set the maximum value of the progress bar */
203 progressBar->setMaximum(totalBytes);
204 /* set currency */
205 progressBar->setValue(bytes);
206 }
207
208 /* Network response processing function */
209 void MainWindow::networkReplyError(QNetworkReply::NetworkError
210 error)
211 {
212 switch (error) {
213 case QNetworkReply::ConnectionRefusedError:
214 qDebug()<<"The remote server refused to connect"<<endl;
215 break;
216 case QNetworkReply::HostNotFoundError:
217 qDebug()<<"Remote host name not found"<<endl;
218 break;
219 case QNetworkReply::TimeoutError:
220 qDebug()<<"The connection to the remote server timed out"<<endl;
221 break;
222 default:
223 break;
224 }
225 }

Line 89, the global variable networkAccessManager is instantiated.
From lines 101 to 133, first obtain the URL link from the single line input box as newUrl to judge the validity of the link. Then create the local variable networkrequest and set the networkrequest request URL to newUrl. QNetworkReply*newReply = networkAccessManager->get(networkRequest); As the most important code, all operations responding to this time are handed over to newreply. The corresponding operation is processed through the signal slot.
Lines 135 ~ 173, this part of the code reads the data downloaded from the network from the newReply stream.
Lines 160 to 167, the editor did some processing here. The data downloaded from the network may encounter data loss or download error. This example is to download a JPG image from Baidu, because the judgment basis of JPG image is that the data of the first byte and the second byte are 0xff and 0xd8, and the data of the penultimate first byte and the penultimate second byte are 0xd9 and 0xff respectively. If both are correct, it is judged that the data is JPG picture data. Then save, otherwise the picture will not be saved. Data processing is often necessary. We often have to process the downloaded data.
Lines 174-197, network response completed. Remember to delete reply to prevent memory leakage. If the picture is downloaded successfully, the picture will be displayed on label[0].
Lines 209-225, network response error handling function.

2. Program running effect

After clicking the download button, you can see that the downloaded image has been saved to the path of the current compilation output, called "downloaded. jpg", and displayed on the interface. Because the file download speed is very fast, the download progress bar suddenly becomes 100%. If you want to see the download progress bar, you can modify this example to download other files. Be careful not to save them as jpg images.

Topics: C++ Qt Qt5