您的位置:首页 > 财经 > 金融 > 苏州保洁公司哪家比较好_电子请柬免费模板下载_郑州seo关键词优化公司_4414站长平台

苏州保洁公司哪家比较好_电子请柬免费模板下载_郑州seo关键词优化公司_4414站长平台

2025/3/31 14:45:24 来源:https://blog.csdn.net/2301_80163789/article/details/146547721  浏览:    关键词:苏州保洁公司哪家比较好_电子请柬免费模板下载_郑州seo关键词优化公司_4414站长平台
苏州保洁公司哪家比较好_电子请柬免费模板下载_郑州seo关键词优化公司_4414站长平台

Qt的网络编程

使用 Qt 的网络编程 API 需要先在 .pro 文件中添加 network 模块。Qt 的模块提供了静态库和动态库两个版本。

不默认包含网络等其他模块,是为了使 Qt 生成的可执行程序更加轻量化。

#.pro
QT			+=core gui network
#添加到如上位置

Qt 封装了自己的网络编程接口,将 Qt 的信号和槽的机制运用到了 Qt 的网络编程接口上,这些接口的使用方法与 C 语言原生的套接字编程接口区别较大,尤其是信号和槽的机制使得 Qt 服务器设计可很少使用甚至不使用多线程,也能实现正常的服务器功能。但由于服务器一般都不会做成图形化界面,或者说不会使用 Qt 进行编写,也就无所谓 Qt 的服务器设计了。

1. UDP Socket

Qt 提供了两个类,用 QUdpSocket 来表示一个 UDP 的 socket 文件,用 QNetworkDatagram 来表示一个 UDP 数据报。

QUdpSocket:

声明类型说明对标原生 API
bind(constQHostAddress&, quint16)方法绑定指定的端口号。bind()
QNetworkDatagram receiveDatagram()方法读取一个 UDP 数据报。recvfrom()
writeDatagram(const QNetworkDatagram&)方法发送一个 UDP 数据报。sendto
readyRead信号在收到数据并准备就绪后触发。

quint16 是 Qt 提供的短整型类型,由于 C++ 并未规定 short int 的大小,于是 Qt 为了解耦自创了自己的短整型类型。

QNetworkDatagram:

声明类型说明对标原生 API
QNetworkDatagram(const QByteArray&,const QHostAddress&,quint16)构造函数构造一个 UDP 数据报,通常用于发送数据时使用。
QByteArray data()方法获取数据报内部持有的数据。
senderAddress()方法获取数据报中包含的对端 IP 地址。
senderPort()方法获取数据报中包含的对端端口号。

QString 提供了一个 QByteArray 的构造,可以使用 QString 直接赋值。

1.1 UDP回显服务器

注意 Qt 的信号和槽提供了一种类似于多路复用 IO 的机制,当对端发送数据到来,本机准备读取时,就会触发 readyRead 信号并调用相关槽函数(自定义)。

1.1.1 服务器

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QNetworkDatagram>
#include <QUdpSocket>QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;QUdpSocket* socket;void processRequest();QString process(const QString& request);};
#endif // MAINWINDOW_H

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);socket = new QUdpSocket(this);this->setWindowTitle("服务器");connect(socket,&QUdpSocket::readyRead,this,&MainWindow::processRequest);bool ret = socket->bind(QHostAddress::Any,6666);if(!ret){QMessageBox::critical(this,"端口号绑定失败",socket->errorString());}
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::processRequest()
{const QNetworkDatagram& requestDatagram = socket->receiveDatagram();QString request = requestDatagram.data();const QString& response = process(request);QNetworkDatagram responseDatagram(response.toUtf8(),requestDatagram.senderAddress(),requestDatagram.senderPort());socket->writeDatagram(responseDatagram);QString log = "[" + requestDatagram.senderAddress().toString()+ ":" +QString::number(requestDatagram.senderPort())+"] req: "+request+"resp: "+response;ui->listWidget->addItem(log);
}
QString MainWindow::process(const QString& request)
{return request;
}
1.1.2 客户端

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QNetworkDatagram>
#include <QUdpSocket>QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void on_pushButton_clicked();private:Ui::MainWindow *ui;QUdpSocket* socket;void processResponse();};
#endif // MAINWINDOW_H

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);socket = new QUdpSocket(this);this->setWindowTitle("客户端");connect(socket,&QUdpSocket::readyRead,this,&MainWindow::processResponse);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::processResponse()
{const QNetworkDatagram& responseDatagram = socket->receiveDatagram();QString response = responseDatagram.data();ui->listWidget->addItem("服务器:"+response);
}void MainWindow::on_pushButton_clicked()
{if(ui->lineEdit->text()==""){return;}const QString& text = ui->lineEdit->text();QNetworkDatagram requestDatagram(text.toUtf8(),QHostAddress("127.0.0.1"),6666);socket->writeDatagram(requestDatagram);ui->listWidget->addItem("客户端:"+text);ui->lineEdit->setText("");
}

2. TCP Socket

Qt 提供了两个类,用 QTcpServer 来监听端口,获取客户端连接。用 QTcpSocket 来实现客户端和服务器之间的数据交换。

QTcpServer:

声明类型说明对标原生 API
listen(constQHostAddress&, quint16 port)方法绑定指定的地址和端口号,并开始监听。bind()listen()
nextPendingConnection()方法从系统中获取一个已经建立好的 TCP 连接。返回一个 QTcpSocket 表示这个客户端的连接,通过这个 socket 对象完成和客户端之间的通信。accept()
newConnection信号有新的客户端建立连接好之后触发。无,类似于多路复用

QTcpSocket

声明类型说明对标原生 API
QBytearray readAll()方法读取当前接收缓冲区中的所有数据,返回 QBytearray 对象。read()
write(const QByteArray&)方法把数据写入 socket 中。write()
deleteLater方法暂时把 socket 对象标记为无效。Qt 会在下个事件循环中析构释放该对象。
readyRead信号有数据到达并准备就绪时就触发。
disconnected信号连接断开时触发。

2.1 TCP回显服务器

注意,回显服务器的设计没有考虑到粘包问题,在实际的服务器设计中,应当自定义应用层协议,在报文里写好本次发送的数据包总大小,并使用一个足够大的字节数组作为缓冲区来接收,然后按照协议的方法解析报文。

2.1.1 服务器

mainwindow.h

注意服务器是使用 QTcpServer 类型作为成员变量监听套接字,当接收到新的连接之后,再创建 QTcpSocket 变量作为通信的套接字使用。

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QTcpServer>QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;QTcpServer* tcpServer;void processConnection();QString process(const QString request);
};
#endif // MAINWINDOW_H

mainwindow.cpp:

每有一个连接来到,服务器就会有一个 QTcpSocket* 对象,随着客户端越来越多,如果不释放就会释放就会导致严重的内存泄漏问题。但是手动 delete 并不可行,因为槽函数都是依赖这个对象来进行操作的,最好使用 Qt 提供的 deleteLater() 接口,它会在下次事件循环时,将对象释放掉。

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include <QTcpSocket>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);this->setWindowTitle("服务器");tcpServer = new QTcpServer(this);connect(tcpServer,&QTcpServer::newConnection,this,&MainWindow::processConnection);bool ret = tcpServer->listen(QHostAddress::Any,6666);if(!ret){QMessageBox::critical(this,"服务器启动失败!",tcpServer->errorString());exit(1);}
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::processConnection()
{QTcpSocket* clientSocket = tcpServer->nextPendingConnection();QString log ="[" + clientSocket->peerAddress().toString()+ ":" + QString::number(clientSocket->peerPort())+"]客户端上线!";ui->listWidget->addItem(log);connect(clientSocket,&QTcpSocket::readyRead,this,[=](){QString request = clientSocket->readAll();const QString& response = process(request);clientSocket->write(response.toUtf8());QString log = "[" + clientSocket->peerAddress().toString()+":"+QString::number(clientSocket->peerPort())+"] req: "+request+",resp: "+response;ui->listWidget->addItem(log);});connect(clientSocket,&QTcpSocket::disconnected,this,[=](){QString log = "[" + clientSocket->peerAddress().toString()+":"+QString::number(clientSocket->peerPort())+"]客户端下线!";ui->listWidget->addItem(log);//手动释放 clientSocket,使用Qt提供的方法,在下一次事件循环时释放clientSocket->deleteLater();});
}QString MainWindow::process(const QString request)
{return request;
}
2.1.2 客户端

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QTcpSocket>QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void on_pushButton_clicked();private:Ui::MainWindow *ui;QTcpSocket* socket;
};
#endif // MAINWINDOW_H

mainwindow.cpp

connectToHost() 是在进行三次握手。

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);this->setWindowTitle("客户端");socket = new QTcpSocket(this);socket->connectToHost("127.0.0.1",6666);connect(socket,&QTcpSocket::readyRead,this,[=](){QString response = socket->readAll();ui->listWidget->addItem(" 服务器:"+response);});bool ret=socket->waitForConnected();if(!ret){QMessageBox::critical(this,"服务器连接出错",socket->errorString());exit(1);}}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{const QString& text = ui->lineEdit->text();if(text==""){return;}socket->write(text.toUtf8());ui->listWidget->addItem("客户端:"+text);ui->lineEdit->setText("");
}

3. HTTP

Qt 提供了 HTTP 客户端的库,但没有提供服务器的库。Qt 的 HTTP 库并没有像浏览器那样解析 HTML ,进行类似于网页浏览的操作。一般用于通过 HTTP 从服务器获取数据或向服务器提交数据。

Qt 的 API 主要是三个类包含的,QNetworkAccessManagerQNetworkRequestQNetworkReply

QNetworkAccessManager 提供了 HTTP 的核心操作:

方法说明
QNetworkReply get(constQNetworkRequest&)发起一个 HTTP GET 请求。
post(const QNetworkRequest&,const QByteArray&)发起一个 HTTP POST 请求。

QNetworkRequest 表示一个 HTTP 请求:

注意 QNetworkRequest 是不包含 body 的,body 需要通过 setHeader() 来包含。

方法说明
QNetworkRequest(constQUrl&)通过 URL 构造一个 HTTP 请求。
setHeader(QNetworkRequest::KnownHeaders header,const QVariant& value)设置请求头。

QVariant 表示一个类型可变的值,类似于 C 语言中的 void*

QNetworkRequest::KnownHeaders 是一个枚举变量,常用取值:

取值说明
ContentTypeHeader描述 body 的类型。
ContentLengthHeader描述 body 的长度。
LocationHeader用于重定向报文中指定重定向地址。
CookieHeader设置 cookie。
UserAgentHeader设置 User-Agent。

QNetworkReply 表示一个 HTTP 响应:

方法说明
error()获取出错状态。
errorString()获取出错原因的文本。
readAll()读取响应 body。
header(QNetworkRequest::KnownHeaders header)读取指定 header 的值。

注意 QNetworkReply 的方法一般都是非阻塞的,即执行流不会等待方法获取请求或响应完成后再执行剩下的代码,Qt 通过提供了 finished 信号,这个信号会在客户端收到完整的响应数据之后触发。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com