[Qt first entered the rivers and lakes] Qt QUdpSocket underlying architecture and principle detailed description

Yuxian: CSDN content partner, CSDN new star mentor, 51CTO (Top celebrity + expert blogger), github open source enthusiast (secondary development of go-zero source code, game back-end architecture https://github.com/Peakchen)

Qt QUdpSocket is a client-side and server-side class used to implement the UDP protocol. It can send and receive datagrams (Datagram), and provides a wealth of methods and signals to send and receive data. The underlying architecture of QUdpSocket consists of the following parts:

 

  1. QUdpSocketPrivate

QUdpSocketPrivate is a private class of QUdpSocket, which is responsible for the underlying implementation of QUdpSocket. QUdpSocketPrivate contains a member variable of QAbstractSocketEngine type, which is used to manage the underlying socket communication. In the constructor of QUdpSocket, a QUdpSocketPrivate object is created and used as the private data of QUdpSocket.

  1. QAbstractSocketEngine

QAbstractSocketEngine is an abstract class that defines the interface of QUdpSocket underlying socket communication. The specific socket communication implementation is implemented by the QAbstractSocketEngine subclass of each platform. In QUdpSocketPrivate, a specific QAbstractSocketEngine subclass object will be created to implement the underlying socket communication of QUdpSocket.

  1. QSocketNotifier

QSocketNotifier is a class used in Qt to notify file descriptor status changes. It can monitor the read and write status of file descriptors and send signals to notify applications when the status changes. In QUdpSocketPrivate, QSocketNotifier is used to monitor the read and write status of the underlying socket communication, and a signal is sent to notify QUdpSocket when the status changes.

The following is the basic implementation architecture diagram of QUdpSocket:

+-------------------+
|      QUdpSocket    |
+-------------------+
| - socketDescriptor|
+-------------------+
          /_\
           |
           | 继承
           |
+-------------------+
|      QAbstractSocket |
+-------------------+
| - socketState     |
| - socketError     |
| - readBuffer      |
| - writeBuffer     |
| - readNotifier    |
| - writeNotifier   |
| - exceptionNotifier|
+-------------------+
          /_\
           |
           | 继承
           |
+-------------------+
|       QObject     |
+-------------------+

It can be seen that QUdpSocket inherits from two classes QAbstractSocket and QObject, among which QAbstractSocket provides basic socket operations (such as connecting, disconnecting, sending data, etc.), while QObject provides basic functions of objects (such as signals and slot, object name, etc.).

In the QUdpSocket class, the socketDescriptor attribute represents the socket descriptor used by the bottom layer, which is an integer value. In the constructor of QUdpSocket, a new socket will be created through the socket() function and associated with the socketDescriptor property. The bind() function of QUdpSocket will call the underlying bind() function to bind the socket to the specified local address and port.

QUdpSocket monitors the readable and writable events of the socket through the readNotifier and writeNotifier member variables. When the socket has data to read or write data, it will trigger the corresponding signal (such as readyRead and bytesWritten signal) to notify application. When a socket error occurs, the socketError signal will be triggered, and the application can handle the error through this signal.

In QUdpSocket, we can use the following methods to realize the communication between UDP client and server:

  1. bind()

The bind() method is used to bind QUdpSocket to the specified IP address and port number, so that it can receive datagrams from the IP address and port number. It accepts a parameter of type QHostAddress, indicating the IP address to be bound, and an integer parameter, indicating the port number to be bound. For example:

QUdpSocket socket;
socket.bind(QHostAddress::Any, 1234);
  1. writeDatagram()

The writeDatagram() method is used to send datagrams to the specified IP address and port number. It takes a byte array as a parameter representing the data to send, a parameter of type QHostAddress representing the IP address to send to, and an integer parameter representing the port number to send to. For example:

QUdpSocket socket;
QByteArray data("Hello, world!");
socket.writeDatagram(data, QHostAddress::Broadcast, 1234);
  1. readDatagram()

The readDatagram() method is used to receive datagrams from QUdpSocket. It accepts two reference parameters, one is a reference of type QByteArray, indicating the received data, the other is a reference of type QHostAddress, indicating the IP address of the sender, and an integer reference, indicating the port number of the sender. For example:

QUdpSocket socket;
QByteArray buffer;
QHostAddress sender;
quint16 senderPort;
socket.readDatagram(buffer.data(), buffer.size(), &sender, &senderPort);
  1. close()

The close() method is used to close the socket, it does not accept any parameters. For example:

QUdpSocket socket;
socket.close();
  1. error()

The error() method is used to get the error code of the socket, which returns a value of QAbstractSocket::SocketError enumeration type. For example:

QUdpSocket socket;
socket.bind(QHostAddress::Any, 1234);
if (socket.waitForReadyRead(5000)) {
    QByteArray buffer;
    QHostAddress sender;
    quint16 senderPort;
    socket.readDatagram(buffer.data(), buffer.size(), &sender, &senderPort);
    qDebug() << "Received datagram from" << sender.toString() << ":" << senderPort;
} else {
    qDebug() << "Error:" << socket.error();
}

The above are the main methods of the QUdpSocket class. In addition, there are many other methods and signals that can be used according to actual needs. The following is a simple sample code that demonstrates how to use QUdpSocket to realize the communication between UDP client and server.

UDP server side code:

#include <QtCore>
#include <QtNetwork>

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QUdpSocket socket;
    socket.bind(QHostAddress::Any, 1234);

    QObject::connect(&socket, &QUdpSocket::readyRead, [&socket](){
        QByteArray buffer;
        buffer.resize(socket.pendingDatagramSize());

        QHostAddress sender;
        quint16 senderPort;

        socket.readDatagram(buffer.data(), buffer.size(), &sender, &senderPort);

        qDebug() << "Received datagram from" << sender.toString() << ":" << senderPort;
        qDebug() << "Data:" << buffer;
    });

    return app.exec();
}

UDP client code:

#include <QtCore>
#include <QtNetwork>

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QUdpSocket socket;

    QByteArray data("Hello, world!");
    socket.writeDatagram(data, QHostAddress::LocalHost, 1234);

    return app.exec();
}

In the above example, the UDP server uses the bind() method to bind the QUdpSocket to the local IP address and port number 1234, and then uses the readyRead() signal to monitor the arrival of data. When data arrives, it will use the readDatagram() method to receive the data, and output the IP address and port number of the sender, as well as the received data.

The UDP client uses the writeDatagram() method to send datagrams to the local IP address, with the destination port number being 1234.

UDP is a connectionless protocol, so there is no need to establish a connection when using QUdpSocket to send and receive data. Each sending and receiving of data is an independent operation and will not be affected by previous or subsequent data.

Guess you like

Origin blog.csdn.net/feng1790291543/article/details/131806963