【Qt网络】—— Qt网络编程

目录

(一)UDP Socket

1.1 核心API概览

 1.2  代码示例

1.2.1 回显服务器

1.2.2 回显客户端 

 (二)TCP Socket

2.1 核心API概览

2.2 代码示例

2.2.1 回显服务器

2.2.2 回显客户端

(三)HTTP Client

3.1 核心API

3.2 代码示例 

(四)其他模块

总结


和多线程类似,Qt为了支持跨平台,对网络编程的API也进行了重新封装。咱们接下来的重点介绍Qt的网络相关的API的使用。

注意: 

  • 实际Qt开发中进行网络编程,也不⼀定使用Qt封装的网络API,也有⼀定可能使用的是系统原⽣API或者其他第三方框架的API. 

在进行网络编程之前,需要在项目中的 .pro 文件中添加 network 模块. 添加之后要手动编译⼀下项目,使QtCreator能够加载对应模块的头文件.


(一)UDP Socket

1.1 核心API概览

主要的类有两个. QUdpSocket QNetworkDatagram

QUdpSocket 表示⼀个UDP的socket⽂件.

QNetworkDatagram 表⽰⼀个UDP数据报.


 1.2  代码示例

1.2.1 回显服务器

  • 1) 创建界面,包含⼀个 QListWidget 用来显示消息.

  • 2) 创建 QUdpSocket 成员 .修改widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QUdpSocket>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void handle();QString process(const QString& request);
private:Ui::Widget *ui;QUdpSocket* socket;};
#endif // WIDGET_H

修改widget.cpp,完成socket后续的初始化

  • ⼀般来说,要先连接信号槽,再绑定端口.
  • 如果顺序反过来,可能会出现端口绑定好了之后,请求就过来了.此时还没来得及连接信号槽.那么这 个请求就有可能错过了. 
#include "widget.h"
#include "ui_widget.h"
#include<QMessageBox>
#include<QNetworkDatagram>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 1. 设置窗⼝标题this->setWindowTitle("服务器");//实例化 socketsocket = new QUdpSocket(this);//连接信号槽connect(socket,&QUdpSocket::readyRead,this,&Widget::handle);//绑定端口号bool res = socket->bind(QHostAddress::Any,9090);if (!res) {QMessageBox::critical(this, "服务器启动出错", socket->errorString());return;}
}Widget::~Widget()
{delete ui;
}

3)实现handle,完成处理请求的过程 

  • 读取请求并解析
  • 根据请求计算响应
  • 把响应写回到客户端
void Widget::handle()
{// 1. 读取请求const QNetworkDatagram& requestDatagram = socket->receiveDatagram();QString request = requestDatagram.data();// 2. 根据请求计算响应const QString& response = process(request);// 3. 把响应写回到客⼾端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);
}

4) 实现 process相关功能

  •  由于我们此处是实现回显服务器.所以process⽅法中并没有包含实质性的内容. 
QString Widget::process(const QString &request)
{return request;
}

到此 服务器程序编写完毕. 


1.2.2 回显客户端 

1) 创建界面.包含⼀个 QLineEdit ,QPushButton和QListWidget

  • 先使用水平布局把 QPushButton , QLineEdit 和 sizePolicy 为 QListWidget QPushButton 放好,并设置这两个控件的垂直方向的 Expanding •
  • 再使用垂直布局把 QListWidget 和上面的水平布局放好.
  • 设置垂直布局的 layoutStretch 为 5, 1 (当然这个尺寸比例根据个人喜好微调).

2) 在widget.cpp中,先创建两个全局常量,表示服务器的IP和端口

// 提前定义好服务器的 IP 和 端⼝
const QString& SERVER_IP = "127.0.0.1";
const quint16 SERVER_PORT = 9090;

3) 创建 QUdpSocket 成员 修改widget.h,定义成员

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include<QUdpSocket>
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 handle();
private:Ui::Widget *ui;// 创建 socket 成员QUdpSocket* socket;
};
#endif // WIDGET_H

修改widget.cpp,初始化socket

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 1. 实例化 socketsocket = new QUdpSocket(this);// 2. 设置窗⼝名字this->setWindowTitle("客户端");connect(socket,&QUdpSocket::readyRead,this,&Widget::handle);
}

4) 给发送按钮slot函数,实现发送请求. 

void Widget::on_pushButton_clicked()
{// 1. 获取到输⼊框的内容const QString& text = ui->lineEdit->text();// 2. 构造请求数据QNetworkDatagram requestDatagram(text.toUtf8(),QHostAddress(SERVER_IP),SERVER_PORT);// 3. 发送请求socket->writeDatagram(requestDatagram);// 4. 消息添加到列表框中ui->listWidget->addItem("客户端:" + text);// 5. 清空输⼊框ui->lineEdit->setText("");
}void Widget::handle()
{//读取响应数据const QNetworkDatagram& responseDatagram = socket->receiveDatagram();QString response = responseDatagram.data();//把响应数据显示到界面上ui->listWidget->addItem(QString("服务器说: ") + response);
}

最终执行效果(记住先运行服务器在运行客户端


 (二)TCP Socket

2.1 核心API概览

核心类是两个: QTcpServer QTcpSocket

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

QTcpSocket 用户客户端和服务器之间的数据交互.

QByteArray⽤用于表⽰⼀个字节数组.可以很方便的和QString进行相互转换.

例如:

  • 使用QString的构造函数即可把QByteArray转成QString.
  • 使用QString的 toUtf8 函数即可把QString转成QByteArray. 

2.2 代码示例

2.2.1 回显服务器

1)创建界面.包含⼀个 QListWidget ,用于显示收到的数据

2) 创建 QTcpServer 并初始化 修改widget.h,添加 QTcpServer 指针成员 

 class Widget : public QWidget{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:Ui::Widget *ui;//创建QTcpServer QTcpServer* tcpServer;};

修改widget.cpp,实例化QTcpServer 并进行后续初始化操作. 

  • 设置窗口标题
  • 实例化TCPserver.(父元素设为当前控件,会在父元素销毁时被⼀起销毁).
  • 通过信号槽,处理客户端建立的新连接.
  • 监听端口
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//设置窗口标题this->setWindowTitle("服务器");//初始化tcpserver = new QTcpServer(this);//通过槽函数连接connect(tcpserver,&QTcpServer::newConnection,this,&Widget::handle);//监听端口bool res = tcpserver->listen(QHostAddress::Any,9090);if(!res){QMessageBox::critical(this,"服务器启动失败",tcpserver->errorString());exit(1);}
}

3) 继续修改widget.cpp,实现处理连接的具体方法 handle

  • 获取到新的连接对应的socket.
  • 通过信号槽,处理收到请求的情况
  • 通过信号槽,处理断开连接的情况
void Widget::handle()
{//获取到新的连接对应的socket.QTcpSocket* clientSocket = tcpserver->nextPendingConnection();QString log = QString('[') + 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 = QString("[") + 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->deleteLater();});
}

 4) 实现process方法,实现根据请求处理响应. 由 于我们此处是实现回显服务器.所以process⽅法中并没有包含实质性的内容.

QString Widget::process(const QString &request)
{return request;
}

到此,服务器程序编写完毕.


2.2.2 回显客户端

1) 创建界面.包含⼀个 QLineEdit ,QPushButton和QListWidget

  • 先使用水平布局把 QPushButton , QLineEdit 和 sizePolicy 为 QListWidget QPushButton 放好,并设置这两个控件的垂直方向的 Expanding •
  • 再使用垂直布局把 QListWidget 和上面的水平布局放好.
  • 设置垂直布局的 layoutStretch 为 5, 1 (当然这个尺寸比例根据个人喜好微调).

 2) 创建 QUdpSocket 成员 修改widget.h,定义成员

class Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_pushButton_clicked();private:Ui::Widget *ui;QTcpSocket* socket;
};
#endif // WIDGET_H

 修改widget.cpp,初始化socket

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 1. 设置窗⼝标题.this->setWindowTitle("客户端");// 2. 实例化 socket 对象.socket = new QTcpSocket(this);// 3. 和服务器建⽴连接.socket->connectToHost("127.0.0.1", 9090);// 4.处理服务器返回的响应.connect(socket,&QTcpSocket::readyRead,this,[=](){//读取当前接收缓冲区中的所有数据QString response = socket->readAll();qDebug() << response;ui->listWidget->addItem(QString("服务器说: ") + response);});// 5. 等待并确认连接是否出错if(!socket->waitForConnected()){QMessageBox::critical(this,"连接服务器出错!",socket->errorString());exit(1);}
}

 3) 给发送按钮slot函数,实现发送请求. 

void Widget::on_pushButton_clicked()
{// 获取输⼊框的内容const QString& text = ui->lineEdit->text();// 清空输⼊框内容ui->lineEdit->setText("");// 把消息显⽰到界⾯上ui->listWidget->addItem(QString("客⼾端说: ") +text);// 发送消息给服务器socket->write(text.toUtf8());
}

先启动服务器,再启动客户端(可以启动多个),最终执行效果:


(三)HTTP Client

进行Qt开发时,和服务器之间的通信很多时候也会用到HTTP协议.

  • 通过HTTP从服务器获取数据.
  • 通过HTTP向服务器提交数据

3.1 核心API

关键类主要是三个:QNetworkAccessManager , QNetworkRequest , QNetworkReply

QNetworkAccessManager 提供了HTTP的核心操作.

QNetworkRequest 表示⼀个HTTP请求(不含body).

其中的 QNetworkRequest::KnownHeaders 是⼀个枚举类型,常用取值: 

QNetworkReply 表⽰⼀个HTTP响应.这个类同时也是 QIODevice 的⼦类

此外, 发. QNetworkReply 还有⼀个重要的信号 finished 会在客户端收到完整的响应数据之后触发。


3.2 代码示例 

1) 创建界面.包含⼀个 QLineEdit ,QPushButton和QListWidget

  • 先使用水平布局把 QPushButton , QLineEdit 和 sizePolicy 为 QListWidget QPushButton 放好,并设置这两个控件的垂直方向的 Expanding •
  • 再使用垂直布局把 QListWidget 和上面的水平布局放好.
  • 设置垂直布局的 layoutStretch 为 5, 1 (当然这个尺寸比例根据个人喜好微调).

【说明】

  • 此处建议使用 QPlainTextEdit 而不是 QTextEdit .主要因为 QTextEdit 要进行富文本解析,如果得到的HTTP响应体积很⼤,就会导致界面渲染缓慢甚至被卡住。

2) 修改widget.h,创建 QNetworkAccessManager 属性

#include <QWidget>
#include<QNetworkAccessManager>
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;
};
#endif // WIDGET_H

3) 修改widget.cpp,创建实例

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);this->setWindowTitle("客户端");// 实例化属性manager = new QNetworkAccessManager(this);
}Widget::~Widget()
{delete ui;
}

4) 编写按钮的slot函数,实现发送HTTP请求功能. 

void Widget::on_pushButton_clicked()
{// 1. 获取到输⼊框中的 URL, 构造 QUrl 对象QUrl url(ui->lineEdit->text());// 2. 构造 HTTP 请求对象QNetworkRequest request(url);// 3. 发送 GET 请求QNetworkReply* response = manager->get(request);// 4. 通过信号槽来处理响应connect(response, &QNetworkReply::finished, this, [=]() {if (response->error() == QNetworkReply::NoError) {// 响应正确QString html(response->readAll());ui->plainTextEdit->setPlainText(html);// qDebug() << html;} else {// 响应出错ui->plainTextEdit->setPlainText(response->errorString());}response->deleteLater();});
}

执行程序,观察效果 


(四)其他模块

 Qt 中还提供了FTP,DNS,SSL等网络相关的组件工具.此处不再⼀⼀展开介绍.有需要的可以自行翻阅官方文档学习相关API的使用.


总结

Qt是一个跨平台的应用程序和用户界面框架,广泛用于开发图形用户界面程序,同时也提供了强大的网络编程能力。Qt的网络编程主要基于其网络模块,即QtNetwork模块。以下是一些关于Qt网络编程的小结:

TCP套接字编程

  • 使用QTcpSocket可以创建客户端和服务器端的TCP连接。客户端通过连接到服务器的IP地址和端口来建立连接,而服务器端则监听特定端口等待客户端的连接请求。

UDP套接字编程

  • QUdpSocket用于处理无连接的网络通信。它允许发送和接收UDP数据包。UDP不保证数据包的顺序或可靠性,但其开销较小,适用于对实时性要求较高的应用。

HTTP请求

  • QNetworkAccessManager是处理HTTP请求的核心类。它支持GET、POST、PUT、DELETE等多种HTTP方法。通过QNetworkRequest可以设置请求的URL、头部信息等,而QNetworkReply用于处理服务器返回的数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/420352.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

虚拟现实智能家居实训系统实训解决方案

随着科技的飞速发展&#xff0c;智能家居已成为现代生活的重要组成部分&#xff0c;它不仅极大地提升了居住的便捷性与舒适度&#xff0c;还推动了物联网、大数据、人工智能等前沿技术的融合应用。为了满足市场对智能家居专业人才日益增长的需求&#xff0c;虚拟现实智能家居实…

Mongodb 4.2.25 安装教程

一、上传部署包 1.1上传mongodb包进入/usr/local目录&#xff0c;将mongodb-linux-x86_64-rhel70-4.2.25.tgz包传到该目录下。 cd /usr/local 二、安装 2.1解压 tar zxvf mongodb-linux-x86_64-rhel70-4.2.25.tgz 2.2修改名称 mv mongodb-linux-x86_64-rhel70-4.2.25/ mong…

突破最强算法模型,Transformer !!

这几天&#xff0c;大家对于Transformer的问题&#xff0c;还是不少。 今儿再和大家聊聊~ 简单来说&#xff0c;Transformer 是一种神经网络模型&#xff0c;在机器翻译、语言理解等任务中表现特别好。它的核心思想是自注意力机制&#xff08;Self-Attention&#xff09;&…

反序列化漏洞练习2

拿到题目&#xff0c;发现目标是获得flag.php的内容,且sis中admin和passwd等于sis2407时会输出fag的内容 根据源码编写序列化代码 <?php error_reporting(0); class sis{public $admin;public $passwd;public function __construct(){$this->admin "sis2407"…

【redis】认识redis和分布式系统

文章目录 认识 redisredis 的主要功能实现数据库实现缓存实现消息中间件 基础概念评价指标 分布式系统单机架构为什么数据多了主机就难以应对 &#xff1f;分布式系统 认识 redis redis 的主要功能 用来在内存中存储数据 定义变量不就是在内存中存储数据吗&#xff1f;为什么…

Python计算机视觉 第7章-图像搜索

Python计算机视觉 第7章-图像搜索 7.1 基于内容的图像检索 在大型图像数据库上&#xff0c;CBIR&#xff08;Content-Based Image Retrieval&#xff0c;基于内容的图像检索&#xff09;技术用于检索在视觉上具相似性的图像。这样返回的图像可以是颜色相似、纹理相似、图像中…

halcon try_catch无try不项目

#1&#xff0c;没有用过try的人&#xff0c;肯定是没有真正实战做过项目的。 #2&#xff0c;try_catch又被称为抓异常语句&#xff0c;出现异常的代码会在 exception里进行显示&#xff0c;这个exception就是一个字符串的数组。 #3&#xff0c;为什么要用&#xff0c;halcon的一…

OpenCV结构分析与形状描述符(13)拟合椭圆函数fitEllipseDirect()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆&#xff0c;该椭圆拟合一组2D点。它返回一个内切于该椭圆的旋转矩形。使用了由[91]提出的直接…

将字符串序列中的每个字符串,用字符“0“扩充到x位 Series.str.zfill(x)

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 将字符串序列中的每个字符串sn 如果sn的位数不足x位 则在sn左侧补充0凑齐x位 即在sn左侧补充x-sn个0 Series.str.zfill(x) 选择题 关于以下代码输出结果的说法中正确的是? import pandas …

在国产芯片上实现YOLOv5/v8图像AI识别-【4.4】RK3588网络摄像头推理后推流到RTSP更多内容见视频

本专栏主要是提供一种国产化图像识别的解决方案&#xff0c;专栏中实现了YOLOv5/v8在国产化芯片上的使用部署&#xff0c;并可以实现网页端实时查看。根据自己的具体需求可以直接产品化部署使用。 B站配套视频&#xff1a;https://www.bilibili.com/video/BV1or421T74f 前言…

基于微信小程序点餐、外卖系统的设计与实现 (源码+lw+参考文档+核心代码讲解等)

基于微信小程序点餐、外卖系统的设计与实现(源码lw部署文档讲解等) 项目概述&#xff1a; 这段时间做了一个关于点餐的小程序&#xff0c;也是学习和总结的一部分&#xff0c;希望对大家有所帮助。本课题的主要目标是设计并能够实现一个基于微信小程序点餐系统。项目采用的是…

transforemr网络理解

1.transformer网络中数据的流动过程&#xff1a; 2.transformer中残差的理解&#xff1a; 残差连接&#xff08;Residual Connection&#xff09; 的核心思想就是通过将输入与经过变化的输出相加&#xff0c;来最大限度地保留原始信息。 transforemr中注意力层网络和前馈神经…

GMS地下水数值模拟及溶质(包含反应性溶质)运移模拟技术深度应用

以地下水数值模拟软件GMS操作为主要授课内容&#xff0c;在教学中强调模块化教学&#xff0c;分为前期数据收集与处理&#xff1b;三维地质结构建模&#xff1b;地下水流动模型构建&#xff1b;地下水溶质运移模型构建和反应性溶质运移构建5个模块&#xff1b;采用全流程模式将…

计算机技术专硕,三维数字地球的学习路径?

三维数字地球是一个跨学科领域&#xff0c;涉及地理信息系统&#xff08;GIS&#xff09;、计算机图形学、遥感技术、大数据处理等多个方面。作为计算机技术专硕的学生&#xff0c;可以按照以下学习路径来逐步深入&#xff1a; 1、基础理论学习&#xff1a; 地理信息系统&…

C 408—《数据结构》算法题基础篇—链表(上)

目录 Δ前言 一、链表中特定值结点的删除 0.题目&#xff1a; 1.算法设计思想&#xff1a; 2.C语言描述&#xff1a; 3.算法的时间和空间复杂度&#xff1a; 二、链表链表最小值结点的删除 0.题目 : 1.算法设计思想 : 2.C语言描述 : 3.算法的时间和空间复杂度 : 三、链…

【FPGA数字信号处理】- FIR串行滤波器

理解和掌握 FIR 串行滤波器是踏入数字信号处理领域的重要一步。 那么&#xff0c;什么是 FIR 串行滤波器&#xff1f;它是如何工作的&#xff1f;又有着怎样的神奇之处呢&#xff1f;让我们一起揭开它的神秘面纱。 一、FIR 滤波器简介 FIR 滤波器&#xff0c;全称为有限脉冲…

PointNet++改进策略 :模块改进 | x-Conv | PointCNN, 结合局部结构与全局排列提升模型性能

目录 前言PointCNN实现细节1. X X X-Conv 操作输入输出步骤 2. PointCNN 网络架构层级卷积分类与分割任务 3. 数据增强4. 效率优化 前言 这篇论文介绍了一种名为 PointCNN 的方法&#xff0c;旨在从点云&#xff08;point cloud&#xff09;数据中学习特征。传统卷积神经网络…

【前端】探索webpack3项目build速度优化, 优化个p

文章目录 背景uglifyjs-webpack-pluginwebpack3 压缩混淆js 优化踩坑。结论 背景 webpack3 babel7 uglifyjs-webpack-plugin的项目&#xff0c;build起来是什么体验。 大抵是写了两个月后&#xff0c;发现build时间从120s激增到400s。而这400秒中&#xff0c;有50多秒是Ugli…

江协科技STM32学习- P11 中断系统,EXTI外部中断

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

Pygame中Sprite类实现多帧动画3-3

4 使用自定义类MySprite 使用自定义类MySprite实现多帧动画的步骤是首先创建MySprite类的实例&#xff0c;之后使用相关函数对该实例进行操作。 4.1 创建MySprite类的实例 创建MySprite类的实例的代码如图12所示。 图12 创建MySprite类的实例的代码 其中&#xff0c;变量dr…