当前位置: 首页 > news >正文

Qt——网络通信(UDP/TCP/HTTP)

要使用Qt的网络通信,要先引入Network模块

需要在CMake中添加如下语句:

find_package(Qt6 COMPONENTS Network REQUIRED)
target_link_libraries(your_target_name PRIVATE Qt6::Network)

UDP

主要的类有两个:

QUdpSocket :表示一个UDP的socket文件

名称类型说明对标原生 API
bind(const QHostAddress&, quint16)方法绑定指定的端口号.bind
receiveDatagram()方法返回 QNetworkDatagram,读取一个 UDP 数据报.recvfrom
writeDatagram(const QNetworkDatagram&)方法发送一个 UDP 数据报.sendto
readyRead信号在收到数据并准备就绪后触发.无 (类似于 IO 多路复用的通知机制)

QNetworkDataGram :表示一个UDP的数据包

名称类型说明对标原生 API
QNetworkDatagram(const QByteArray&, const QHostAddress&, quint16)构造函数通过 QByteArray、目标 IP 地址、目标端口号构造一个 UDP 数据报,通常用于发送数据时
data()方法获取数据报内部持有的数据,返回 QByteArray
senderAddress()方法获取数据报中包含的对端的 IP 地址无,recvfrom 包含了该功能
senderPort()方法获取数据报中包含的对端的端口号无,recvfrom 包含了该功能

利用上面的接口,实现一个带有图形化界面的Echo服务器和客户端:

服务器:

// widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QUdpSocket>
#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void handlerRequest();private:bool process(const QString& request, QString* response);private:Ui::Widget *ui;QUdpSocket* socket_ = nullptr;
};
#endif  // WIDGET_H// widget.cpp
#include "widget.h"#include <QMessageBox>
#include <QNetworkDatagram>#include "./ui_widget.h"Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->setWindowTitle("服务器");ui->listWidget->setWindowTitle("服务器日志");socket_ = new QUdpSocket(this);  // 创建UDP套接字// connectconnect(socket_, &QUdpSocket::readyRead, this, &Widget::handlerRequest);// 进行端口绑定if (!socket_->bind(QHostAddress::Any, 8080)) {QMessageBox::critical(this, "服务器启动失败", socket_->errorString());return;}
}Widget::~Widget() { delete ui; }void Widget::handlerRequest() {const QNetworkDatagram data = socket_->receiveDatagram();QString request = data.data();QString response;if (!process(request, &response)) {//...}QNetworkDatagram send_data(response.toUtf8(), data.senderAddress(), data.senderPort());socket_->writeDatagram(send_data);QString log = "[" + data.senderAddress().toString() + ":" + QString::number(data.senderPort()) +"]: " + request;ui->listWidget->addItem(log);
}// 请求处理函数
bool Widget::process(const QString &request, QString *response) {*response = request;return true;
}

客户端:

// widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QUdpSocket>
#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_pushButton_clicked();void handlerResponse();private:Ui::Widget *ui;QUdpSocket *socket_ = nullptr;
};
#endif  // WIDGET_H// widget.cpp
#include "widget.h"#include <QNetworkDatagram>#include "./ui_widget.h"const QString SERVER_IP = "127.0.0.1";
const uint16_t SERVER_PORT = 8080;Widget::Widget(QWidget* parent) : QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->setWindowTitle("客户端");ui->pushButton->setShortcut(Qt::Key_Return);socket_ = new QUdpSocket(this);  // 创建UDP套接字// connectconnect(socket_, &QUdpSocket::readyRead, this, &Widget::handlerResponse);
}Widget::~Widget() { delete ui; }void Widget::on_pushButton_clicked() {QString text = this->ui->lineEdit->text();this->ui->lineEdit->clear();const QNetworkDatagram send_data(text.toUtf8(), QHostAddress(SERVER_IP), SERVER_PORT);socket_->writeDatagram(send_data);
}void Widget::handlerResponse() {const QNetworkDatagram& data = socket_->receiveDatagram();this->ui->listWidget->addItem("RECV: " + data.data());
}

效果:

在这里插入图片描述


TCP

核心的两个类:

QTcpServer,用于监听端口,获取客户端连接:

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

QTcpSocket,用于客户端和服务器的连接以及通信

名称类型说明对标原生 API
readAll()方法读取当前接收缓冲区中的所有数据,返回 QByteArray 对象read
write(const QByteArray&)方法把数据写入 socket 中write
deleteLater方法暂时把 socket 对象标记为无效,Qt 会在下个事件循环中析构释放该对象无(类似于 “半自动化的垃圾回收”)
connectToHost方法客户端连接到服务器,非阻塞connect
waitForConnected方法等待,直到连接建立,阻塞
readyRead信号有数据到达并准备就绪时触发无(类似于 IO 多路复用的通知机制)
disconnected信号连接断开时触发无(类似于 IO 多路复用的通知机制)

利用上面的接口,实现一个带有图形化界面的Echo服务器和客户端:

服务器:

// widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QTcpServer>
#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:void handlerConnection();bool process(const QString& request, QString* response);private:Ui::Widget *ui;QTcpServer* server_ = nullptr;
};
#endif  // WIDGET_H// widget.cpp
#include "widget.h"#include <QMessageBox>
#include <QTcpSocket>#include "./ui_widget.h"Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->setWindowTitle("服务器");// 创建TCP服务器server_ = new QTcpServer(this);// connectconnect(server_, &QTcpServer::newConnection, this, &Widget::handlerConnection);// 绑定 + 监听if (!server_->listen(QHostAddress::Any, 8080)) {QMessageBox::critical(this, "服务器启动错误", server_->errorString());exit(-1);}
}Widget::~Widget() { delete ui; }void Widget::handlerConnection() {// 获得与客户端通信的socketQTcpSocket *socket = server_->nextPendingConnection();const QString log = "[" + socket->peerAddress().toString() + ":" +QString::number(socket->peerPort()) + "] : " + "连接建立";this->ui->listWidget->addItem(log);// connect 消息处理connect(socket, &QTcpSocket::readyRead, this, [=]() {const QString &data = socket->readAll();  // 接收请求QString response;// 处理请求,构造响应if (!process(data, &response)) {//...}// 返回响应socket->write(response.toUtf8());const QString log = "[" + socket->peerAddress().toString() + ":" +QString::number(socket->peerPort()) + "] : " + "request: " + data +", response: " + response;this->ui->listWidget->addItem(log);});// connect 连接断开connect(socket, &QTcpSocket::disconnected, this, [=]() {const QString log = "[" + socket->peerAddress().toString() + ":" +QString::number(socket->peerPort()) + "] : " + "连接断开";this->ui->listWidget->addItem(log);// 下一个事件循环,自动释放 TcpSocketsocket->deleteLater();});
}bool Widget::process(const QString &request, QString *response) {*response = request;return true;
}

客户端:

// widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QTcpSocket>
#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_pushButton_clicked();private:Ui::Widget *ui;QTcpSocket *client_ = nullptr;
};
#endif  // WIDGET_H// widget.cpp
#include "widget.h"#include <QMessageBox>#include "./ui_widget.h"const QString SERVER_IP = "127.0.0.1";
const uint16_t SERVER_PORT = 8080;Widget::Widget(QWidget* parent) : QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->setWindowTitle("客户端");ui->pushButton->setShortcut(Qt::Key_Return);ui->lineEdit->setPlaceholderText("请输入请求信息");// 创建客户端client_ = new QTcpSocket(this);// connect 消息处理connect(client_, &QTcpSocket::readyRead, this, [=]() {const QString& data = client_->readAll();this->ui->listWidget->addItem("RECV: " + data);});// 连接服务器,并等待建立成功client_->connectToHost(QHostAddress(SERVER_IP), SERVER_PORT);if (!client_->waitForConnected()) {QMessageBox::critical(this, "连接服务器失败", client_->errorString());exit(-1);}
}Widget::~Widget() { delete ui; }void Widget::on_pushButton_clicked() {const QString& send_data = ui->lineEdit->text();ui->lineEdit->clear();client_->write(send_data.toUtf8());
}

效果:

在这里插入图片描述


HTTP

关键类:

QNetworkAccessManage ,提供了HTTP的核心操作

方法说明
get(const QNetworkRequest& )发起一个 HTTP GET 请求,返回 QNetworkReply 对象
post(const QNetworkRequest& , const QByteArray& )发起一个 HTTP POST 请求,返回 QNetworkReply 对象

QNetworkRequest ,表示一个HTTP请求,不包含body

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

QNetworkReply ,表示一个HTTP响应

方法说明
error()获取出错状态
errorString()获取出错原因的文本
readAll()读取响应 body
header(QNetworkRequest::KnownHeaders header)读取响应指定 header 的值
deleteLater暂时把对象标记为无效,Qt 会在下个事件循环中析构释放该对象

其还有一个信号finished,会在客户端收到完整的响应数据后触发

例如:

#ifndef WIDGET_H
#define WIDGET_H#include <QNetworkAccessManager>
#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_pushButton_clicked();private:Ui::Widget *ui;QNetworkAccessManager *manager_ = nullptr;
};
#endif  // WIDGET_H// widget.cpp
#include "widget.h"#include <QNetworkReply>
#include <QNetworkRequest>#include "./ui_widget.h"Widget::Widget(QWidget* parent) : QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->setWindowTitle("HTTP Client");ui->lineEdit->setPlaceholderText("请输入要获取的 URL");ui->pushButton->setShortcut(Qt::Key_Return);ui->plainTextEdit->setReadOnly(true);// 创建 http 管理器manager_ = new QNetworkAccessManager(this);
}Widget::~Widget() { delete ui; }void Widget::on_pushButton_clicked() {const QString& url = ui->lineEdit->text();ui->lineEdit->clear();// 构造请求QNetworkRequest request{QUrl(url)};// 发送get请求QNetworkReply* reply = manager_->get(request);// connect 相应获取处理方法connect(reply, &QNetworkReply::finished, this, [=]() {if (reply->error() == QNetworkReply::NoError) {QString data = reply->readAll();this->ui->plainTextEdit->setPlainText(data);} else {this->ui->plainTextEdit->setPlainText(reply->errorString());}// 获取到响应后,就需要释放资源reply->deleteLater();});
}

效果:

在这里插入图片描述

http://www.dtcms.com/a/348599.html

相关文章:

  • 【Unity开发】Unity核心学习(二)
  • PAT 1081 Rational Sum
  • 【机器学习】8 Logistic regression
  • Power BI切片器自定义顺序
  • 智能油脂润滑系统:给设备一份 “私人定制” 的保养方案
  • Linux 学习笔记 - 集群管理篇
  • 【大模型LLM学习】Data Agent学习笔记
  • C++算法学习专题:二分查找
  • Kubernetes部署Prometheus+Grafana 监控系统NFS存储方案
  • Socket some functions
  • 让机器人“想象”未来?VLN导航迎来“理解力”新升级
  • 每日算法刷题Day64:8.24:leetcode 堆6道题,用时2h30min
  • 解密 Spring Boot 自动配置:原理、流程与核心组件协同
  • 人形机器人——电子皮肤技术路线:压电式电子皮肤及一种超越现有电子皮肤NeuroDerm的设计
  • 深度学习:CUDA、PyTorch下载安装
  • Leetcode 3659. Partition Array Into K-Distinct Groups
  • sqlite创建数据库,创建表,插入数据,查询数据的C++ demo
  • 商密保护迷思:经营秘密到底需不需要鉴定?
  • 对称二叉树
  • 机械学习综合练习项目
  • jar包项目自启动设置ubuntu
  • [论文阅读] 软件工程 | GPS算法:用“路径摘要”当向导,软件模型检测从此告别“瞎找bug”
  • 服务器硬件电路设计之 SPI 问答(四):3 线 SPI、Dual SPI 与 Qual SPI 的奥秘
  • 春秋云镜 Hospital
  • Vue 3多语言应用开发实战:vue-i18n深度解析与最佳实践
  • 线程包括哪些状态?线程状态之间是如何变化的?
  • yggjs_rlayout框架v0.1.2使用教程 02 TechLayout 布局组件
  • 拿AI下围棋 -- 开源项目leela-zero
  • ​Mac用户安装JDK 22完整流程(Intel版dmg文件安装指南附安装包下载)​
  • mysql历史社区版本下载