Reference: Qt Creator quick start, Third Edition, edited by Huo Yafei
1,UDP
UDP(User Datagram Protocol) is a lightweight, unreliable, datagram oriented and connectionless protocol, which is used when reliability is not very important. UDP is generally divided into sender and receiver.
QUdpSocket class is used to send and receive UDP datagrams, which is inherited from QAbstractSocket class. Socket here is the so-called "socket". In short, "socket" is an IP address plus a port number.
1.1 UDP programming example
The following is a UDP programming example, which realizes the following functions: the sender specifies the port number, enters the content to be sent, and clicks the broadcast button to send. The receiving end specifies the receiving port number and displays the received data.
1.1.1 UDP sender
The main interface used by the sender is qint64 qudpsocket:: writedatagram (const char * data, qint64 len, const qhostaddress & host, quant16 port);, This function is used to send datagrams.
Sender ui
Sending end full header file
#ifndef SENDER_H #define SENDER_H #include <QDialog> QT_BEGIN_NAMESPACE namespace Ui { class Sender; } QT_END_NAMESPACE class QUdpSocket; class Sender : public QDialog { Q_OBJECT public: Sender(QWidget *parent = nullptr); ~Sender(); private slots: void on_pushButton_clicked(); private: Ui::Sender *ui; QUdpSocket* sender; }; #endif // SENDER_H
The sender is complete cpp file
#include "sender.h" #include "ui_sender.h" #include <QUdpSocket> #include <QtNetwork> Sender::Sender(QWidget *parent) : QDialog(parent) , ui(new Ui::Sender) { ui->setupUi(this); sender=new QUdpSocket(this); } Sender::~Sender() { delete ui; } void Sender::on_pushButton_clicked() { QByteArray datagram=ui->textEdit->toPlainText().toLocal8Bit(); sender->writeDatagram(datagram.data(),datagram.size(),QHostAddress::Broadcast,ui->spinBox->value()); }
1.1.2 udp receiver
Main interfaces used at the receiving end:
Bool qabstractsocket:: bind (quint16 port = 0, bindmode = defaultforplatform) bind port. This interface does not need to specify an IP address. It supports all IPv4 IP addresses by default. The port number specified in the first parameter should be consistent with the sender. The second parameter is the binding mode. QUdpSocket::ShareAddress indicates that other servers are allowed to bind to the same address and port.
Whenever a datagram arrives, QUdpSocket will send readyRead() signal, so that data can be read in a customized slot.
bool QUdpSocket::hasPendingDatagrams() const judge whether there is still data waiting to be read.
Qint64 qudpsocket:: readdatagram (char * data, qint64 maxlen, qhostaddress * host = q_nullptr, qint16 * port = q_nullptr) receives data.
Receiver ui
The receiver is complete h file
#ifndef RECEIVER_H #define RECEIVER_H #include <QDialog> QT_BEGIN_NAMESPACE namespace Ui { class Receiver; } QT_END_NAMESPACE class QUdpSocket; class Receiver : public QDialog { Q_OBJECT public: Receiver(QWidget *parent = nullptr); ~Receiver(); private slots: void processPendingDatagram(); void on_spinBox_valueChanged(int arg1); private: Ui::Receiver *ui; QUdpSocket* receiver; }; #endif // RECEIVER_H
The receiver is complete cpp file
#include "receiver.h" #include "ui_receiver.h" #include<QtNetwork> Receiver::Receiver(QWidget *parent) : QDialog(parent) , ui(new Ui::Receiver) { ui->setupUi(this); receiver=new QUdpSocket(this); receiver->bind(ui->spinBox->value(),QUdpSocket::ShareAddress); connect(receiver,&QUdpSocket::readyRead,this,&Receiver::processPendingDatagram); } Receiver::~Receiver() { delete ui; } void Receiver::processPendingDatagram() { //Have waiting datagrams while(receiver->hasPendingDatagrams()) { QByteArray datagram; //Let the size of datagram be the size of datagram waiting to be processed, so as to receive complete data datagram.resize(receiver->pendingDatagramSize()); //Receive datagrams and store them in datagram receiver->readDatagram(datagram.data(),datagram.size()); //ui->label->setText(datagram); ui->textBrowser->setText(QString::fromLocal8Bit( datagram)); } } void Receiver::on_spinBox_valueChanged(int arg1) { receiver->close(); receiver->bind(arg1,QUdpSocket::ShareAddress); }
1.1.3 operation effect
2,TCP
TCP (Transmission Control Protocol) is a low-level network protocol for data transmission. Many Internet Protocols (including HTTP and FTP) are based on TCP protocol. TCP is a reliable transmission protocol for data flow and connection.
The QTcpSocket class also inherits from the QAbstractSocket class. Unlike the datagram transmitted by QUdpSocket, QTcpSocket transmits continuous data stream, which is especially suitable for continuous data transmission. TCP programming is generally divided into client and server, that is, the so-called C/S (Client/Server) model.
Before any data transmission, a TCP connection must be established to the remote host and port.
QTcpSocket works asynchronously and reports status changes and error messages by transmitting signals.
You can use QTcpSocket::write() function to write data and QTcpSocket::read() function to read data. Before reading data from a QTcpSocket, you must first call the QTcpSocket::bytesAvailable() function to ensure that there is enough data available.
If you want to handle the incoming TCP connection, you can use the qtcpsocket class to call the listen() function to set up the server, and then associate the newConnection() signal to the custom slot, which will be transmitted whenever there is a client connection. Then call the nextPendingConnection() in the custom slot to receive the connection, and use the QTcpSocket object returned by the function to communicate with the client. (see the Server::acceptConnection slot function in the following example code for details)
2.1 TCP programming example
The following is a TCP programming example, which realizes the function of realizing the transmission of large files and displaying the transmission progress.
2.1.1 tcp client
Main functions and implementation methods of the client: specify the server address and port number to be connected from the ui interface, and select the file to be sent. After clicking the send button, call the void QAbstractSocket::connectToHost() function to connect to the server. When connected to the server, it receives the QAbstractSocket::connected() signal, which is associated with the custom slot startTransfer() in advance, and calls the qint64 QTcpSocket::write() to send the file header structure in the custom slot. After each transmission, a byteswriten signal will be received, which will be associated with the user-defined slot updateClientProgress slot. The data will be sent in blocks in the slot function and the progress bar will be updated.
Client ui
Client integrity h file
#ifndef CLIENT_H #define CLIENT_H #include <QDialog> #include <QAbstractSocket> QT_BEGIN_NAMESPACE namespace Ui { class Client; } QT_END_NAMESPACE class QTcpSocket; class QFile; class Client : public QDialog { Q_OBJECT public: Client(QWidget *parent = nullptr); ~Client(); private slots: void openFile(); void send(); void startTransfer(); void updateClientProgress(qint64); void displayError(QAbstractSocket::SocketError error); void on_openButton_clicked(); void on_sendButton_clicked(); private: Ui::Client *ui; QTcpSocket* tcpClient; QFile* localFile;//Files to send qint64 totalBytes;//Total size of sent data qint64 bytesWritten;//Size of data sent qint64 bytesToWrite;//Remaining data size qint64 payloadSize;//Size of data sent each time QString fileName;//Save file path QByteArray outBlock;//Data buffer, that is, it stores the data blocks to be sent each time }; #endif // CLIENT_H
Client integrity cpp file
#include "client.h" #include "ui_client.h" #include <QtNetwork> #include <QFileDialog> Client::Client(QWidget *parent) : QDialog(parent), ui(new Ui::Client) { ui->setupUi(this); payloadSize=64*1024;//64KB totalBytes=0; bytesWritten=0; bytesToWrite=0; tcpClient=new QTcpSocket(this); //When the connection to the server is successful, a connected signal will be sent to start the file transfer connect(tcpClient,SIGNAL(connected()),this,SLOT(startTransfer())); connect(tcpClient,SIGNAL(bytesWritten(qint64)),this,SLOT(updateClientProgress(qint64))); connect(tcpClient,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError))); ui->sendButton->setEnabled(false); } Client::~Client() { delete ui; } void Client::openFile() { fileName=QFileDialog::getOpenFileName(this); if(!fileName.isEmpty()) { ui->sendButton->setEnabled(true); ui->clientStatusLabel->setText(QString::fromLocal8Bit("Open file%1 success!").arg(fileName)); } } void Client::send() { ui->sendButton->setEnabled(false); //Initialization sent byte is 0 bytesWritten=0; ui->clientStatusLabel->setText(QString::fromLocal8Bit("Connecting...")); tcpClient->connectToHost(ui->hostLineEdit->text(),ui->portLineEdit->text().toInt()); } void Client::startTransfer() { localFile=new QFile(fileName); if(!localFile->open(QFile::ReadOnly)) { qDebug()<<"client:open file error!"; return; } //Get file size totalBytes=localFile->size(); QDataStream sendOut(&outBlock,QIODevice::WriteOnly); sendOut.setVersion(QDataStream::Qt_5_6); QString currentFileName=fileName.right(fileName.size()-fileName.lastIndexOf('/')-1); //Keep the total size information space, file name size information space, and then enter the file name sendOut<<qint64(0)<<qint64(0)<<currentFileName; //The total size here is the sum of the total size information, file name size information, file name and actual file size totalBytes+=outBlock.size(); //Return to the beginning of outBlock and replace the two qint64(0) spaces with the actual size information sendOut.device()->seek(0); sendOut<<totalBytes<<qint64(outBlock.size()-sizeof(qint64)*2); //The size of the remaining data after sending the file header structure bytesToWrite=totalBytes-tcpClient->write(outBlock); ui->clientStatusLabel->setText(QString::fromLocal8Bit("Connected")); outBlock.resize(0); } void Client::updateClientProgress(qint64 numBytes) { //Size of data sent bytesWritten+=(int)numBytes; //If data has been sent if(bytesToWrite>0) { //Send 64KB of payloadSize data every time. If the remaining data is less than 64K, send the remaining data size outBlock=localFile->read(qMin(bytesToWrite,payloadSize)); //The size of data remaining after sending data once bytesToWrite-=(int)tcpClient->write(outBlock); //Clear send buffer outBlock.resize(0); } else { localFile->close(); } //Update progress bar ui->clientProgressBar->setMaximum(totalBytes); ui->clientProgressBar->setValue(bytesWritten); //If sending is complete if(bytesWritten==totalBytes) { ui->clientStatusLabel->setText(QString::fromLocal8Bit("transfer file%1 success").arg(fileName)); localFile->close(); tcpClient->close(); } } void Client::displayError(QAbstractSocket::SocketError error) { qDebug()<<tcpClient->errorString(); tcpClient->close(); ui->clientProgressBar->reset(); ui->clientStatusLabel->setText(QString::fromLocal8Bit("Client ready")); ui->sendButton->setEnabled(true); } void Client::on_openButton_clicked() { ui->clientProgressBar->reset(); ui->clientStatusLabel->setText(QString::fromLocal8Bit("Status: waiting to open file")); openFile(); } void Client::on_sendButton_clicked() { send(); }
2.1.2 tcp server side
The main function and implementation of the server side: click the "start monitor" button, then call bool QTcpServer:: listen (const QHostAddress &address = QHostAddress:: Any, quint16 port = 0) interface to open the monitor. Associate QTcpServer::newConnection to the custom slot acceptConnection(). In the acceptConnection() slot function, receive the incoming connection request and obtain its socket tcpserverconnection = tcpserver nextPendingConnection(); Then, the readyRead signal is associated to the updateserverprogress () slot. In the updateServerProgress() slot function, first receive the header structure information such as total number, file name, size and file name respectively, then receive the actual file, and then update the progress bar. The main interfaces used are tcpserverconnection - > bytesavailable(), tcpserverconnection - > readall()
Server side ui
Server integrity h file
#ifndef SERVER_H #define SERVER_H #include <QDialog> #include <QAbstractSocket> #include <QTcpServer> QT_BEGIN_NAMESPACE namespace Ui { class Server; } QT_END_NAMESPACE class QTcpSocket; class QFile; class Server : public QDialog { Q_OBJECT public: Server(QWidget *parent = nullptr); ~Server(); private slots: void start(); void acceptConnection(); void updateServerProgress(); void displayError(QAbstractSocket::SocketError socketError); void on_startButton_clicked(); private: Ui::Server *ui; QTcpServer tcpServer; QTcpSocket* tcpServerConnection; qint64 totalBytes;//Store total size information qint64 bytesReceived;//Size of received data qint64 fileNameSize;//File name and size information QString fileName;//Storage file name QFile* localFile;//Local file QByteArray inBlock;//Data buffer }; #endif // SERVER_H
Server integrity cpp file
#include "server.h" #include "ui_server.h" #include <QtNetwork> Server::Server(QWidget *parent) : QDialog(parent) , ui(new Ui::Server) { ui->setupUi(this); connect(&tcpServer,SIGNAL(newConnection()),this,SLOT(acceptConnection())); } Server::~Server() { delete ui; } void Server::start() { if(!tcpServer.listen(QHostAddress::LocalHost,6666)) { qDebug()<<tcpServer.errorString(); close(); return; } ui->startButton->setEnabled(false); totalBytes=0; bytesReceived=0; fileNameSize=0; ui->ServerStatusLabel->setText(QString::fromLocal8Bit("monitor")); ui->serverProgressBar->reset(); } void Server::acceptConnection() { tcpServerConnection=tcpServer.nextPendingConnection(); connect(tcpServerConnection,SIGNAL(readyRead()),this,SLOT(updateServerProgress())); connect(tcpServerConnection,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError))); ui->ServerStatusLabel->setText(QString::fromLocal8Bit("Receive connection")); //Turn off the server and stop listening tcpServer.close(); } void Server::updateServerProgress() { QDataStream in(tcpServerConnection); in.setVersion(QDataStream::Qt_5_6); //If the received data is less than 16 bytes, save the incoming file header structure if(bytesReceived<=sizeof (qint64)*2) { if((tcpServerConnection->bytesAvailable()>=sizeof(qint64)*2) &&(fileNameSize==0)) { //Receive total data size information and file name size information in>>totalBytes>>fileNameSize; bytesReceived+=sizeof(qint64)*2; } if((tcpServerConnection->bytesAvailable()>=fileNameSize)&&(fileNameSize!=0)) { //Receive file name and create file in>>fileName; ui->ServerStatusLabel->setText(QString::fromLocal8Bit("receive files%1...").arg(fileName)); bytesReceived+=fileNameSize; localFile=new QFile(fileName); if(!localFile->open(QFile::WriteOnly)) { qDebug()<<"server:open file error!"; return; } } else { return; } } //If the received data is less than the total number, write to the file if(bytesReceived<totalBytes) { bytesReceived+=tcpServerConnection->bytesAvailable(); inBlock=tcpServerConnection->readAll(); localFile->write(inBlock); inBlock.resize(0); } ui->serverProgressBar->setMaximum(totalBytes); ui->serverProgressBar->setValue(bytesReceived); //When receiving data is complete if(bytesReceived==totalBytes) { tcpServerConnection->close(); localFile->close(); ui->startButton->setEnabled(true); ui->ServerStatusLabel->setText(QString::fromLocal8Bit("receive files%1 success").arg(fileName)); } } void Server::displayError(QAbstractSocket::SocketError socketError) { qDebug()<<tcpServerConnection->errorString(); tcpServerConnection->close(); ui->serverProgressBar->reset(); ui->ServerStatusLabel->setText(QString::fromLocal8Bit("Server ready")); ui->startButton->setEnabled(true); } void Server::on_startButton_clicked() { start(); }
2.1.3 operation effect