Qt网络编程

前言

Qt为了支持跨平台,对系统网络编程的API(socket API)也进行了重新分装。

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

若要使用Qt中的网络编程的API,则要在项目中的.pro文件中添加network模块。

我们学过的Qt的各种控件等常用的功能都是包含在QtCore模块中的,系统默认添加了,为啥Qt要和划分出这些模块呢?Qt本身就是一个非常庞大的框架,如果把所有的Qt功能都放在一起时,即使我们的写的代码非常简单,此时生成的可执行程序也很庞大,因为包含了大量没有使用的功能

进行网络编程的时候,本质上是在编写应用层代码,需要传输层(操作系统内核实现好的)提供支持,socket API就是传输层提供给应用层的接口,传输层最核心的协议有UDP(无连接,不可靠传输,面向数据报,全双工)和TCP(有连接,可靠传输,面向字节流,全双工),并且这两协议差别还挺大,就导致在编写代码的时候也是有所差别的,因此操作系统就提供了两套socket API,所以Qt也提供了两套API,来完成UDP和TCP的开发。

一、UDP Socket

1、核心API

主要的类有两个:QUdpSocket 和 QNetworkDatagram,其中QUdpSocket表示一个UDP的文件,QNetworkDatagram:由于UDP是面向数据报的,每次传输的时候,发送和接受都是一个完整的NetworkDatagram。

QUdpSocket:

当socket收到请求后,QUdpSocket就会触发这个readyRead信号,就可以在信号槽里完成读取请求的操作了。

QNetworkDategram:

2、事例 

写一个带有界面的UDP回显服务器

服务器端

//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:Ui::Widget *ui;//先创建UdpSocket成员QUdpSocket* socket;//服务器的核心逻辑void processRequest();//服务器业务QString process(const QString& request);
};
#endif // WIDGET_H
//Widget.cpp#include "widget.h"
#include "ui_widget.h"#include<QMessageBox>
#include<QNetworkDatagram>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//创建出这个对象socket = new QUdpSocket(this);this->setWindowTitle("服务器");//连接信号槽connect(socket,&QUdpSocket::readyRead,this,&Widget::processRequest);//绑定端口号,必须在绑定端口号之前链接信号槽//如果在绑定完成之后,连接信号槽之前的话,若有客户端把请求发过来的,//此时就可能读不到这样的请求//QHostAddress::Any表示不管有多少网卡都可绑定上去,//端口号可自己写但要在范围内(0~65536)bool ret = socket->bind(QHostAddress::Any,9090);//一个端口号只能被一个socket绑定,若9090被别人绑定的话就会失败if(!ret){//绑定失败QMessageBox::critical(this,"服务器启动出错",socket->errorString());return;}}Widget::~Widget()
{delete ui;
}//服务器的核心逻辑
void Widget::processRequest()
{//1、读取数据const QNetworkDatagram& requestDatagram = socket->receiveDatagram();//2、解析QString request = requestDatagram.data();//虽然它返回的是QBetyArry,其是可以赋值给QString//3、根据请求计算响应(回显服务器,响应不需要计算,就是请求本身)const QString& response = process(request);//4、把响应发送给客户端QNetworkDatagram responseDategram(response.toUtf8(), requestDatagram.senderAddress(), requestDatagram.senderPort());socket->writeDatagram(responseDategram);// 把这次交互的信息, 显示到界面上.QString log = "[" + requestDatagram.senderAddress().toString() + ":" + QString::number(requestDatagram.senderPort())+ "] req: " + request + ", resp: " + response;ui->listWidget->addItem(log);
}QString Widget::process(const QString &request)
{return request;
}

客户端

//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();private:Ui::Widget *ui;QUdpSocket* socket;void processResponse();
};
#endif // WIDGET_H
//Widget.cpp#include "widget.h"
#include "ui_widget.h"#include<QNetworkDatagram>const QString& SERVER_IP  = "127.0.0.1";
const quint16 SERVER_PORT = 9090;Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);socket = new QUdpSocket(this);this->setWindowTitle("客户端");//连接槽函数connect(socket,&QUdpSocket::readyRead,this,&Widget::processResponse);}Widget::~Widget()
{delete ui;
}void Widget::processResponse()
{// 通过这个函数来处理收到的响应.// 1. 读取到响应数据const QNetworkDatagram& responseDatagram = socket->receiveDatagram();QString response = responseDatagram.data();// 2. 把响应数据显示到界面上.ui->listWidget->addItem("服务器说: " + response);
}void Widget::on_pushButton_clicked()
{//1、获取到输入框的内容const QString& text = ui->lineEdit->text();//2、构造UDP的请求数据QNetworkDatagram requestDatagram(text.toUtf8(),QHostAddress(SERVER_IP), SERVER_PORT);//3发送请求数据socket->writeDatagram(requestDatagram);// 4. 把发送的请求也添加到列表框中.ui->listWidget->addItem("客户端说: " + text);// 5. 把输入框的内容也清空一下.ui->lineEdit->setText("");}

二、TCP Socket

1、核心API

核心类有:QTcpServer 和 QTcpSocket。

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

QTcpSocket用于客户端和服务端之间的数据交互 。

 peerAddress和peerPort用于获取对端的地址和端口,

connectToHost("地址",端口号);和服务器建立连接,

waitForConnected(),等待连接建立的结果,确认是否连接成功,返回bool类型

 QByteArry用于表示一个字节数组,可以很方便的和QString进行相互转换,使用QString的构造函数可以把QByteArray转成QString,使用QString的toUtf8 函数可以把QString转成QByteArray

2、事例(写一个TCP回显服务器)

在学习liunx时写的TCP的回显服务器的时候,遇到了一个问题就是多个客户端访问的时候就只有一个生效,引入多线程给每个客户端安排一个单独的线程问题才解决 

服务端

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

//Widget.cpp#include "widget.h"
#include "ui_widget.h"#include<QTcpSocket>
#include<QMessageBox>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//修改窗口标题this->setWindowTitle("服务器");//实例化QUdpsocket对象tcpServer = new QTcpServer(this);//绑定槽函数,当有新的客户端建立好连接后connect(tcpServer,&QTcpServer::newConnection,this,&Widget::processConnection);//绑定+监听//这和操作得是初始化的最后一步,都是需要把如何处理连接,如何处理请求等都准备好后才能真正绑定端口并监听bool ret = tcpServer->listen(QHostAddress::Any,9090);if(!ret){QMessageBox::critical(this,"服务器启动失败",tcpServer->errorString());return;}
}Widget::~Widget()
{delete ui;
}void Widget::processConnection()
{//通过tcpServer拿到一个socket对象,通过这个对象来和客户端进行通信QTcpSocket* clientSocket = tcpServer->nextPendingConnection();//显示用户上线                      //对端端口号QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "]" + "客户端上线了";ui->listWidget->addItem(log);//如果有数据到达并准备就绪时,通过信号槽来处理客户端发送请求的情况connect(clientSocket,&QTcpSocket::readyRead,this,[=](){//读取请求数据QString req = clientSocket->readAll();//根据请求进行响应QString resp = process(req);//把响应写回客户端clientSocket->write(resp.toUtf8());//上述操作记录到日志中QString log = "[" + clientSocket->peerAddress().toString() + QString::number(clientSocket->peerPort()) + "]"+ "res:" + req + "resp:" + resp;ui->listWidget->addItem(log);});//当客户端断开时connect(clientSocket,&QTcpSocket::disconnected,this,[=](){//显示日志QString log = "[" + clientSocket->peerAddress().toString() + ":" +QString::number(clientSocket->peerPort()) + "]" + "客户端下线";ui->listWidget->addItem(log);//手动释放clientSocket//在下一轮事件循环在进行销毁操作clientSocket->deleteLater();});
}QString Widget::process(const QString req)
{return req;
}

客户端

//Widget.h#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpSocket>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* tcpSocket;
};
#endif // WIDGET_H
//Widget.cpp#include "widget.h"
#include "ui_widget.h"#include<QMessageBox>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//设置窗口标题this->setWindowTitle("客户端");//创建socket实例对象tcpSocket = new QTcpSocket(this);//和服务器建立连接tcpSocket->connectToHost("127.0.0.1",9090);//连接信号槽处理响应connect(tcpSocket,&QTcpSocket::readyRead,this,[=](){//读取响应内容QString resp = tcpSocket->readAll();//把响应内容显示到界面上ui->listWidget->addItem("服务器说:" + resp);});//等待连接建立的结果,确认是否连接成功bool ret = tcpSocket->waitForConnected();if(!ret){QMessageBox::critical(this,"连接服务器出错",tcpSocket->errorString());exit(1);}
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{QString text = ui->lineEdit->text();ui->lineEdit->clear();//把请求发送到服务端tcpSocket->write(text.toUtf8());//把发送的消息显示到界面上ui->listWidget->addItem("客户端说:" + text);
}

三、HTTP client

HTTP的使用相比于TCP/UDP更多一些,TCP/UDP是传输层的协议,所以还要在应用层搭配程序员自定义的协议完成工作,而HTTP是一个现成的应用协议,本身就提供了一些方便的拓展能力以及特殊功能,所以更多的时候用HTTP完成数据的交互。

HTTP协议本质上就是基于TCP协议实现的,所以实现一个HTTP客户端/服务器也就是基于TCP Socket进行了封装,Qt中只是提供了HTTP客户端,而没有提供HTTP服务器的库。

1、核心API

关键类主要有三个:QNetworkAccessManager、QNetworkRequest、QNetworkReply

QNetworkAccessManger提供了HTTP的核心操作

 QNetworkRequest表示一个HTTP请求(不含body),如果需要发送带有body的请求(比如post)会在QNetworkAccessManger的post方法中通过单独的参数来传入body

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

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

QNetworkReply表示一个HTTP 响应.这个类同时也是QIODevice的子类。

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

 2、事例(给服务器发送一个get请求)

//Widget.h#ifndef WIDGET_H
#define WIDGET_H#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
//Widget.cpp#include "widget.h"
#include "ui_widget.h"#include<QUrl>
#include<QNetworkRequest>
#include<QNetworkReply>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);this->setWindowTitle("客户端");manager = new QNetworkAccessManager(this);}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{// 1. 获取到输入框中的 url.QUrl url(ui->lineEdit->text());// 2. 构造一个 HTTP 请求对象QNetworkRequest request(url);// 3. 发送请求QNetworkReply* response = manager->get(request);// 4. 通过信号槽, 来处理响应connect(response, &QNetworkReply::finished, this, [=]() {if (response->error() == QNetworkReply::NoError) {// 响应正确获取到了.QString html = response->readAll();ui->plainTextEdit->setPlainText(html);} else {// 响应出错了ui->plainTextEdit->setPlainText(response->errorString());}// 还需要对 response 进行释放.response->deleteLater();});
}

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

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

相关文章

矩阵短剧系统:如何用1个后台管理100+小程序?技术解析与实战应用

引言&#xff1a;短剧行业的效率革命 2025年&#xff0c;短剧市场规模已突破千亿&#xff0c;但传统多平台运营模式面临重复开发成本高、用户数据分散、内容同步效率低等痛点。行业亟需一种既能降本增效又能聚合流量的解决方案——“矩阵短剧系统”。通过“1个后台管理100小程…

嵌入式STM32学习——ESP8266 01S的基础介绍

简介 ESP8266 系列模组是深圳市安信可科技有限公司开发的一系列基于乐鑫ESP8266EX的低功耗UART-WiFi芯片模组&#xff0c;可以方便地进行二次开发&#xff0c;接入云端服务&#xff0c;实现手机3/4G全球随时随地的控制&#xff0c;加速产品原型设计。 模块核心处理器 ESP8266 在…

form-create-designer中$inject参数的数据结构及各项属性说明

FcDesigner 是一款基于Vue的开源低代码可视化表单设计器工具&#xff0c;通过数据驱动表单渲染。可以通过拖拽的方式快速创建表单&#xff0c;提高开发者对表单的开发效率&#xff0c;节省开发者的时间。并广泛应用于在政务系统、OA系统、ERP系统、电商系统、流程管理等领域。 …

Jasypt概述及整合SpringBoot实现敏感数据加密

前言 在实际开发中&#xff0c;Spring Boot应用的配置文件中经常包含数据库密码、API密钥等敏感信息。如果这些信息以明文形式存储&#xff0c;会带来严重的安全隐患。本文将详细介绍如何使用Jasypt&#xff08;Java Simplified Encryption&#xff09;对Spring Boot配置文件中…

Better Faster Large Language Models via Multi-token Prediction 原理

目录 模型结构&#xff1a; Memory-efficient implementation&#xff1a; 实验&#xff1a; 1. 在大规模模型上效果显著&#xff1a; 2. 在不同类型任务上的效果&#xff1a; 为什么MLP对效果有提升的几点猜测&#xff1a; 1. 并非所有token对生成质量的影响相同 2. 关…

git merge解冲突后,add、continue提交

git merge解冲突后&#xff0c;add、continue提交 git merge操作冲突后&#xff0c;需要手动解冲突&#xff0c;解完冲突后&#xff0c;需要: git add . 然后&#xff0c;进入一般的正常git代码提交流程。 git合并‘merge’其他分支的个别文件到当前branch_gitbash 合并branc…

3.8.1 利用RDD实现词频统计

在本次实战中&#xff0c;我们通过Spark的RDD实现了词频统计功能。首先&#xff0c;准备了包含单词的文件并上传至HDFS。接着&#xff0c;采用交互式方式逐步完成词频统计&#xff0c;包括创建RDD、单词拆分、映射为二元组、按键归约以及排序等操作。此外&#xff0c;还通过创建…

应对进行性核上性麻痹,健康护理铸就温暖防线

进行性核上性麻痹&#xff08;PSP&#xff09;是一种罕见的神经退行性疾病&#xff0c;主要影响患者的运动、平衡及吞咽等功能。针对这类患者&#xff0c;有效的健康护理对提升其生活质量、延缓病情发展至关重要。 在日常生活护理方面&#xff0c;由于患者存在平衡障碍和肌肉僵…

融合蛋白质语言模型和图像修复模型,麻省理工与哈佛联手提出PUPS ,实现单细胞级蛋白质定位

蛋白质亚细胞定位&#xff08;subcellular localization of a protein&#xff09;是指蛋白质在细胞结构中具体的定位情况&#xff0c; 这对蛋白质行使其生物学功能至关重要。举个简单例子&#xff0c;如果把细胞想象成一个庞大的企业&#xff0c;其中细胞核、线粒体、细胞膜等…

lanqiaoOJ 4330:欧拉函数模板

【题目来源】 https://www.lanqiao.cn/problems/4330/learning/ 【问题描述】 这是一道模板题。 首先给出欧拉函数的定义&#xff1a;即 φ(n) 表示的是小于等于 n 的数中和 n 互质的数的个数。 比如说 φ(6)2&#xff0c;当 n 是质数的时候&#xff0c;显然有φ(n)n-1。 【题…

无人机电子防抖技术要点概述!

一、技术要点 1. 传感器数据融合 电子防抖需结合陀螺仪、加速度计、视觉传感器等多源数据&#xff0c;实时检测无人机的姿态变化和振动频率。例如&#xff0c;IMU&#xff08;惯性测量单元&#xff09;通过加速度计和陀螺仪测量飞行器的姿态和运动状态&#xff0c;结合视觉感…

Win10 安装单机版ES(elasticsearch),整合IK分词器和安装Kibana

一. 先查看本机windows是否安装了ES(elasticsearch)&#xff0c;检查方法如下&#xff1a; 检查进程 按 Ctrl Shift Esc 组合键打开 “任务管理器”。在 “进程” 选项卡中&#xff0c;查看是否有 elasticsearch 相关进程。如果有&#xff0c;说明系统安装了 ES。 检查端口…

BIO、NIO、AIO 的区别与实战应用解析

导语&#xff1a; BIO、NIO 和 AIO 是后端面试中的经典话题&#xff0c;尤其在高并发、高性能场景下更是重中之重。本文将从面试官视角出发&#xff0c;深入剖析三者的区别、典型题目和实战解答&#xff0c;助你掌握答题技巧&#xff0c;轻松拿下这一高频考点&#xff01; 一、…

电脑风扇转速不正常的原因

一、硬件故障或接触问题 1. 风扇本身损坏 扇叶卡顿或轴承磨损&#xff1a;灰尘堆积、异物缠绕&#xff08;如头发、线缆&#xff09;会导致扇叶转动阻力增大&#xff0c;发出异响并转速下降&#xff1b;轴承润滑脂干涸或老化会引起风扇噪音大、转速不稳定。电机故障&#xff…

运维打铁:生产服务器用户权限管理方案全解析

文章目录 一、引言二、方案设计2.1 权限模型选择2.2 角色定义2.3 权限分配2.4 用户与角色关联 三、相关代码注释&#xff08;以 Linux 系统为例&#xff09;3.1 用户创建与角色分配脚本3.2 权限设置脚本 四、常见问题解决4.1 用户无法登录4.2 用户权限不足4.3 权限文件修改后不…

在tp6模版中加减法

实际项目中&#xff0c;我们经常需要标签变量加减运算的操作。但是&#xff0c;在ThinkPHP中&#xff0c;并不支持模板变量直接运算的操作。幸运的是&#xff0c;它提供了自定义函数的方法&#xff0c;我们可以利用自定义函数解决&#xff1a;ThinkPHP模板自定义函数语法如下&a…

Fastjson利用链JdbcRowSetImpl分析

首先创建客户端 package com.yq1ng.vul;import com.alibaba.fastjson.JSON;/*** FastJsonTest** author yq1ng* date 2021/12/29 19:45* since 1.0.0*/ public class FastJsonTest {public static void main(String[] args) {String ser "{\"type\":\"co…

基于OAuth2-proxy和Keycloak为comfyui实现SSO

背景 comfyui无认证被漏扫后易被rce挖矿 攻击过程 https://www.oschina.net/news/340226 https://github.com/comfyanonymous/ComfyUI/discussions/5165 阿里云漏洞库关于comfyui的漏洞 https://avd.aliyun.com/search?qcomfyui&timestamp__1384n4%2BxBD0GitGQ0QD8ID%2F…

第R7周:糖尿病预测模型优化探索

文章目录 1.数据预处理1.1 设置GPU1.2 数据导入1.3 数据检查 2. 数据分析2.1 数据分布分析2.2 相关性分析 3. LSTM模型3.1 划分数据集3.2 数据集构建3.3 定义模型 4. 训练模型4.1 定义训练函数4.2 定义测试函数4.3 训练模型 5. 模型评估5.1 Loss与Accuracy图 6. 总结 &#x1f…

一些好用的Chrome 扩展程序

以下是按主要功能分类的 Chrome 扩展程序列表&#xff0c;包括其版本号、中文功能简述以及指向其主页或 Chrome 网上应用店页面的链接。 翻译与语言 沉浸式翻译 - 网页翻译插件 | PDF 翻译 | 免费 版本: 1.16.12 描述: 【沉浸式翻译】免费的&#xff08;原文 / 译文&#xff0…