Qt网络相关

news2025/2/6 0:36:02

“ 所有生而孤独的人,葆有的天真 ” 


         为了⽀持跨平台, QT对⽹络编程的 API 也进⾏了重新封装。本章会上手一套基于QT的网络通信编写

UDP Socket

        在使用Qt进行网络编程前,需要在Qt项目中的.pro文件里添加对应的网络模块( network ).

QT += core gui network

QUdpsocket 核心API

名称
类型
说明
原⽣ API
bind(const QHostAddress&, quint16)
方法
绑定指定的端⼝号
bind
receiveDatagram()
⽅法
返回 QNetworkDatagram . 读取 ⼀个 UDP 数据报.
recvfrom
writeDatagram(const
QNetworkDatagram&)
⽅法
发送⼀个 UDP 数据报.
sendto
readyRead
信号
在收到数据并准备就绪后触发
QNetworkDatagram(const
QByteArray&, const
QHostAddress& , quint16 )
构造函数
data()
⽅法
获取数据报内部持有的数据. 返回QByteArray
senderAddress()
⽅法
获取对端的 IP 地址.
senderPort()
⽅法
获取对端的端⼝号

基于udp的简单回显程序

· 服务端

🎃 创建界面,包含一个 QListWidget 用于显示消息

🎃 在主类中,创建QUdpSocket成员

 🎃 进行初始化

    // 1. 设置窗口标题
    this->setWindowTitle("服务器");

    // 2. 实例化
    socket = new QUdpSocket(this);

    // 3. 连接信号槽, 处理收到的请求
    connect(socket,&QUdpSocket::readyRead,this,&MainWindow::processRequest);

    // 4. 端口bind(ip,port)
    bool ret = socket->bind(QHostAddress::Any,9090);
    if(ret == false){
        QMessageBox::critical(nullptr,"服务器启动错误",socket->errorString());
        return;
    }

        一般来说,都是先建立信号与槽的连接,再进行网络端口的绑定。如果顺序反过来,当网络端口进行bind后,客户端就可以发送来消息处理,此时如果没来得及连接信号槽,为这个请求提供的服务就可能失效。

 🎃 槽函数实现(实现对端消息的回显功能)

QString MainWindow::process(const QString & req)
{
    return req;
}

void MainWindow::processRequest()
{
    // 当走到这里 说明服务器已经收到对端信息递达的信号 触发的槽函数处理~
    // 1.获取请求
    const QNetworkDatagram& requestDatagram = socket->receiveDatagram();
    // QNetworkDatagram.data() 拿到对端请求的原始数据
    QString request = requestDatagram.data();

    // 2. 计算处理请求
    const QString& response = process(request);

    // 3.把响应写回到客⼾端
    QNetworkDatagram responseDatagram(response.toUtf8(),\
                requestDatagram.senderAddress(),requestDatagram.senderPort());
    socket->writeDatagram(responseDatagram);

    // 4.显示打印日志
    QString log = "[" + requestDatagram.senderAddress().toString() + ":"  +
            QString::number(requestDatagram.senderPort()) + "]" + " " + "req: " + request + " | " + "resp: " + response;
    ui->listWidget->addItem(log);
}

· 客户端

🧧 创建一个界面,包含用户发送消息窗口、发送按钮、输入栏。发送窗口仍然使用QListWidget,依次是pushButton、QLineEdit。

        先使⽤⽔平布局( layout) 把两个控件齐整。   

        进入到控件中的sizePolicy 设置为 “Expanding ”。 接着再使用垂直布局,将消息发送窗口与水平布局的两个空间再进行空间管理。当然我们需要在垂直布局中设置比例,否则比较难看~

        这样,简易的界面也算完成了。

🧧 初始化IP和端口

        在mainwindows.h中定义两个静态变量

static const QString& server_ip = "127.0.0.1";
static const quint16 server_port = 9090;
🧧 消息发送实现(槽函数) 
        完成QUdpsocket实例化后,我们只需要关注数据报的发送即可。
void MainWindow::on_send_putton_clicked()
{
    // 1. 获取输入的内容
    const QString& text = ui->message_edit->text();
    // 2. 利用text构造数据报
    QNetworkDatagram requestDatagram = \
            QNetworkDatagram(text.toUtf8(),QHostAddress(server_ip),server_port);
    // 3.发送请求
    socket->writeDatagram(requestDatagram);
    // 4.前端回显
    ui->message_screen->addItem("客户端说: " + text);
    // 5.每发完一条消息 清空输入框
    ui->message_edit->clear();
}

🧧 接收客户端回显(槽函数) 

    // 接收服务端回显
    connect(socket,&QUdpSocket::readyRead,this,[=]()
    {
        const QNetworkDatagram responseDatagram = socket->receiveDatagram();
        QString resp = responseDatagram.data();
        ui->message_screen->addItem(QString("服务器回显: " + resp));
    });

测试:
        
        双方都能看到对端发送的消息,并能及时回显。

TCP Socket

        TCP相对于UDP而言要复杂很多,只要你曾学过网络知识。我们首先来了解了解Tcp Socket中的核心API。

QTcpServer 核心API

名称
类型
说明
对标原⽣ API
listen(const QHostAddress&,
quint16 port)
方法
绑定指定的地址和端⼝号, 并开始监听
bind 和 listen
nextPendingConnection()
方法
从系统中获取到⼀个已经建⽴好的 tcp 连接.
返回⼀个 QTcpSocket , 表⽰这个 客⼾端的连接.
accept
newConnection()
信号
有新的客⼾端建⽴连接好之后触发
readAll()
方法
读取当前接收缓冲区中的所有数据.  返回 QByteArray 对象
read
write(const QByteArray& )
方法
把数据写⼊ socket 中
write
readyRead
信号有数据到达并准备就绪时触发
deleteLater()
方法
暂时把 socket 对象标记为⽆效.
disconnected()
信号
连接断开时触发

        想对比于udp的核心API,我们会发现没有了于 "Datagram" 相关的任何接口了。这本质是因为TCP是面向字节流而非udp那样的数据报。

基于TCP的简单回显程序

        因为都是做回显,那么这里服务器、客户端的前端依旧同udp是一的 ~

· 服务端

🎨  编写QTcpServer

#include <QMainWindow>
#include <QString>
#include <QTcpServer>
#include <QHostAddress>
#include <QMessageBox>
#include <QTcpSocket>


    // 设置窗口信息
    this->setWindowTitle(" 服务器 ");

    // 1.实例化
    tcpserver = new QTcpServer(this);

    // 2.通过信号槽, 处理客⼾端建⽴的新连接.
    connect(tcpserver,&QTcpServer::newConnection,this,&MainWindow::processConnection);

    // 3.监听+bind
    bool ret = tcpserver->listen(QHostAddress::Any,9090);
    if(ret == false){
        QMessageBox::critical(nullptr,"服务器启动失败",tcpserver->errorString());
        exit(-1);
    }

🎨 继续修改 widget.cpp, 实现处理连接的具体⽅法processConnection

void MainWindow::processConnection()
{
    // 1.根据 listen 获取接收到的新连接
    // 注: 这里是 "QTcpSocket"
    QTcpSocket* clientsocket = tcpserver->nextPendingConnection();

    // 更新服务端日志
    QString log = QString("[") + clientsocket->peerAddress().toString() + ":" + \
            QString::number(clientsocket->peerPort()) + "] 客户端上线";
    ui->listWidget->addItem(log);    
}

🎨 完成回显工作(槽函数实现)

        可以发现,不管是做udp还是tcp的网络模型服务,我们都舍弃了用循环的方式处理请求。这会导致我们,一旦存在占用资源的连接不及时释放cpu资源,那么别的请求就不会被读取到,直到该请求的任务执行完成。

        Qt中的槽机制恰好避免了这样的困境,一旦发出信号,就执行槽函数即可~

    // 信号槽处理 处理收到请求的情况
    connect(clientsocket,&QTcpSocket::readyRead,this,[=]()
    {
        // 字节流 把所有字节都 读上来
        const QString req = clientsocket->readAll();
        // 根据请求 制作响应
        const QString& resp = process(req);
        // 写回客户端
        clientsocket->write(resp.toUtf8());

        // 服务端回显
        QString log = "[" + clientsocket->peerAddress().toString() + ":" + QString::number(clientsocket->peerPort()) \
                + "]" + " " + "req: " + req;
        ui->listWidget->addItem(log);
    });

        由于Tcp可靠性的特征,每一次客户端的连接都会被服务端保存着。当客户端断开连接时而服务端并不是释放两者之间用于连接的资源时,就会导致 资源泄漏~~

    // 通过信号槽, 处理断开连接的情况
    connect(clientsocket,&QTcpSocket::disconnected,this,[=]()
    {
        QString log = QString("[") + clientsocket->peerAddress().toString() + ":" + \
                QString::number(clientsocket->peerPort()) + "] 客户端下线";
        ui->listWidget->addItem(log);
        
        // 释放资源
        clientsocket->deleteLater();    // 并不会立即释放
    });

· 客户端

👑 初始化mainwindow.cpp

    this->setWindowTitle("客户端");
    // 1. 实例化socket
    socket = new QTcpSocket(this);
    // 2. 建立连接
    socket->connectToHost("127.0.0.1",9090);
    // 3.等待并确认连接是否出错
    bool ret = socket->waitForConnected();
    if(ret == false){
        QMessageBox::critical(nullptr,"连接失败",socket->errorString());
        exit(-1);
    }

👑 给按钮增加点击的 slot 函数, 实现发送请求给服务器      

void MainWindow::on_send_clicked()
{
    // 获得输入的内容 并输出在界面上
    const QString& text = ui->edit->text();
    ui->edit->clear();
    ui->messageScreen->addItem("已发送: " + text);

    // 真正的发送消息
    socket->write(text.toUtf8());
}
👑  通过信号槽, 处理收到的服务器的响应
    connect(socket,&QTcpSocket::readyRead,this,[=]()
    {
       QString resp = socket->readAll();
       qDebug() << resp;
       ui->messageScreen->addItem("服务端回显: " + resp);
    });

测试:

        不管是响应还是,当客户端断开连接时,我们都能够完成对应的功能。

HTTP 

        进⾏ Qt 开发时, 和服务器之间的通信很多时候也会⽤到 HTTP 协议。我们大概需要以下几个步骤:

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

核心API

        关键类有三个 QNetworkAccessManager、QNetworkRequest、QNetworkReply .

🏀 QNetworkAccessManager 提供了 HTTP 的核⼼操作.
⽅法
说明
get(const QNetworkRequest& )
发起⼀个 HTTP GET 请求. 返回 QNetworkReply 对象.
post(const QNetworkRequest& , const
QByteArray& )
发起⼀个 HTTP POST 请求. 返回 QNetworkReply 对 象.

 

🏀 QNetworkRequest 表⽰⼀个 HTTP 请求.

如果需要发送⼀个带有 body 的请求(⽐如 post), 会在 QNetworkAccessManager 的 post ⽅法 中通过单独的参数来传⼊ body.
⽅法
说明
QNetworkRequest(const QUrl&)
通过 URL 构造⼀个 HTTP 请求.
setHeader(QNetworkRequest::KnownHeaders
header, const QVariant &value)
设置请求头.

🏀 QNetworkRequest::KnownHeaders 是⼀个枚举类型

⽅法
说明
ContentTypeHeader
描述 body 的类型
ContentLengthHeader
描述 body 的⻓度.
LocationHeader
⽤于重定向报⽂中指定重定向地址
CookieHeader
设置 cookie
UserAgentHeader
设置请求头.
🏀 QNetworkReply 表⽰⼀个 HTTP 响应:
⽅法
说明
error()
获取出错状态.
errorString()
获取出错原因的⽂本.
readAll()
读取响应 body
header(QNetworkRequest::KnownHeaders
header)
设置 cookie

构建一个Http客户端

        因为我们只需要构建一个模拟的HTTP请求。服务端则不需要我们进行什么编写~~ 

QPlainTextEdit vs QTextEdit

        QTextEdit会进⾏富 ⽂本解析, 如果得到的 HTTP 响应体积很⼤, 就会导致界⾯渲染缓慢甚⾄被卡住。

🏐 修改 mainwindow.h, 创建 QNetworkAccessManager 属性


#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>

QNetworkAccessManager* manager;

🏐 创建实例并初始化

    this->setWindowTitle(" Http请求发起器 ");
    
    // 1. 实例初始化
    manager = new QNetworkAccessManager(this);
🏐  编写按钮的 slot 函数, 实现发送 HTTP 请求功能.
void MainWindow::on_pushButton_clicked()
{
    // 1. 根据输入框里url,构造Qurl
    QUrl url(ui->lineEdit->text());
    // 2. 构造http响应
    QNetworkRequest req(url);
    // 3. 以什么方法访问
    QNetworkReply* resp = manager->get(req);

    // 通过信号槽来处理响应
    connect(resp,&QNetworkReply::finished,this,[=]()
    {
        if(resp->error() == QNetworkReply::NoError){
            QString html(resp->readAll());
            ui->plainTextEdit->setPlainText(html);
        }
        else{
            ui->plainTextEdit->setPlainText(resp->errorString());
        }
        resp->deleteLater();
    });
}
测试:


本篇到此结束,感谢你的阅读。

祝你好运,向阳而生~

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

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

相关文章

生成式AI安全最佳实践 - 抵御OWASP Top 10攻击 (上)

今天小李哥将开启全新的技术分享系列&#xff0c;为大家介绍生成式AI的安全解决方案设计方法和最佳实践。近年来&#xff0c;生成式 AI 安全市场正迅速发展。据 IDC 预测&#xff0c;到 2025 年全球 AI 安全解决方案市场规模将突破 200 亿美元&#xff0c;年复合增长率超过 30%…

pytorch基于FastText实现词嵌入

FastText 是 Facebook AI Research 提出的 改进版 Word2Vec&#xff0c;可以&#xff1a; ✅ 利用 n-grams 处理未登录词 比 Word2Vec 更快、更准确 适用于中文等形态丰富的语言 完整的 PyTorch FastText 代码&#xff08;基于中文语料&#xff09;&#xff0c;包含&#xff1…

Docker技术相关学习三

一、Docker镜像仓库管理 1.docker仓库&#xff1a;用于存储和分发docker镜像的集中式存储库&#xff0c;开发者可以将自己创建的镜像推送到仓库中也可以从仓库中拉取所需要的镜像。 2.docker仓库&#xff1a; 公有仓库&#xff08;docker hub&#xff09;&#xff1a;任何人都可…

在Mac mini M4上部署DeepSeek R1本地大模型

在Mac mini M4上部署DeepSeek R1本地大模型 安装ollama 本地部署&#xff0c;我们可以通过Ollama来进行安装 Ollama 官方版&#xff1a;【点击前往】 Web UI 控制端【点击安装】 如何在MacOS上更换Ollama的模型位置 默认安装时&#xff0c;OLLAMA_MODELS 位置在"~/.o…

Python 网络爬虫实战:从基础到高级爬取技术

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 1. 引言 网络爬虫&#xff08;Web Scraping&#xff09;是一种自动化技术&#xff0c;利用程序从网页中提取数据&#xff0c;广泛…

python学opencv|读取图像(五十四)使用cv2.blur()函数实现图像像素均值处理

【1】引言 前序学习进程中&#xff0c;对图像的操作均基于各个像素点上的BGR值不同而展开。 对于彩色图像&#xff0c;每个像素点上的BGR值为三个整数&#xff0c;因为是三通道图像&#xff1b;对于灰度图像&#xff0c;各个像素上的BGR值是一个整数&#xff0c;因为这是单通…

控件【QT】

文章目录 控件QWidgetenabledgeometrysetGeometry qrcwindowOpacityQPixmapfonttoolTipfocusPolicystyleSheetQPushButtonRadio ButtionCheck Box显示类控件QProgressBarcalendarWidget 控件 Qt中已经提供了很多内置的控件了(按钮,文本框,单选按钮,复选按钮&#xff0c;下拉框…

STM32 串口发送与接收

接线图 代码配置 根据上一章发送的代码配置&#xff0c;在GPIO配置的基础上需要再配置PA10引脚做RX接收&#xff0c;引脚模式可以选择浮空输入或者上拉输入&#xff0c;在USART配置串口模式里加上RX模式。 配置中断 //配置中断 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE…

【Unity2D 2022:UI】创建滚动视图

一、创建Scroll View游戏对象 在Canvas画布下新建Scroll View游戏对象 二、为Content游戏对象添加Grid Layout Group&#xff08;网格布局组&#xff09;组件 选中Content游戏物体&#xff0c;点击Add Competent添加组件&#xff0c;搜索Grid Layout Group组件 三、调整Grid La…

Python sider-ai-api库 — 访问Claude、llama、ChatGPT、gemini、o1等大模型API

目前国内少有调用ChatGPT、Claude、Gemini等国外大模型API的库。 Python库sider_ai_api 提供了调用这些大模型的一个完整解决方案&#xff0c; 使得开发者能调用 sider.ai 的API&#xff0c;实现大模型的访问。 Sider是谷歌浏览器和Edge的插件&#xff0c;能调用ChatGPT、Clau…

密云生活的初体验

【】在《岁末随笔之碎碎念》里&#xff0c;我通告了自己搬新家的事情。乙巳年开始&#xff0c;我慢慢与大家分享自己买房装修以及在新家的居住体验等情况。 跳过买房装修的内容&#xff0c;今天先说说这三个月的生活体验。 【白河】 潮白河是海河水系五大河之一&#xff0c;贯穿…

Leetcode - 周赛434

目录 一、3432. 统计元素和差值为偶数的分区方案二、3433. 统计用户被提及情况三、3434. 子数组操作后的最大频率四、3435. 最短公共超序列的字母出现频率 一、3432. 统计元素和差值为偶数的分区方案 题目链接 本题可以直接模拟&#xff0c;这里再介绍一个数学做法&#xff0…

C32.【C++ Cont】静态实现双向链表及STL库的list

目录 1.知识回顾 2.静态实现演示图 3.静态实现代码 1.初始双向链表 2.头插 3.遍历链表 4.查找某个值 4.任意位置之后插入元素 5.任意位置之前插入元素 6.删除任意位置的元素 4.STL库的list 1.知识回顾 96.【C语言】数据结构之双向链表的初始化,尾插,打印和尾删 97.【C…

记录一次-Rancher通过UI-Create Custom- RKE2的BUG

一、下游集群 当你的下游集群使用Mysql外部数据库时&#xff0c;会报错&#xff1a; **他会检查ETCD。 但因为用的是Mysql外部数据库&#xff0c;这个就太奇怪了&#xff0c;而且这个检测不过&#xff0c;集群是咩办法被管理的。 二、如果不选择etcd,就选择控制面。 在rke2-…

51单片机入门_05_LED闪烁(常用的延时方法:软件延时、定时器延时;while循环;unsigned char 可以表示的数字是0~255)

本篇介绍编程实现LED灯闪烁&#xff0c;需要学到一些新的C语言知识。由于单片机执行的速度是非常快的&#xff0c;如果不进行延时的话&#xff0c;人眼是无法识别(停留时间要大于20ms)出LED灯是否在闪烁所以需要学习如何实现软件延时。另外IO口与一个字节位的数据对应关系。 文…

99.20 金融难点通俗解释:中药配方比喻马科维茨资产组合模型(MPT)

目录 0. 承前1. 核心知识点拆解2. 中药搭配比喻方案分析2.1 比喻的合理性 3. 通俗易懂的解释3.1 以中药房为例3.2 配方原理 4. 实际应用举例4.1 基础配方示例4.2 效果说明 5. 注意事项5.1 个性化配置5.2 定期调整 6. 总结7. 代码实现 0. 承前 本文主旨&#xff1a; 本文通过中…

6 [新一代Github投毒针对网络安全人员钓鱼]

0x01 前言 在Github上APT组织“海莲花”发布存在后门的提权BOF&#xff0c;通过该项目针对网络安全从业人员进行钓鱼。不过其实早在几年前就已经有人对Visual Studio项目恶意利用进行过研究&#xff0c;所以投毒的手法也不算是新的技术。但这次国内有大量的安全从业者转发该钓…

MYSQL面试题总结(题目来源JavaGuide)

MYSQL基础架构 问题1&#xff1a;一条 SQL语句在MySQL中的执行过程 1. 解析阶段 (Parsing) 查询分析&#xff1a;当用户提交一个 SQL 语句时&#xff0c;MySQL 首先会对语句进行解析。这个过程会检查语法是否正确&#xff0c;确保 SQL 语句符合 MySQL 的语法规则。如果发现…

redis实际开发应用简单实现

短信登录 首先来看看登录与注册常规实现流程如下&#xff1a; 其中&#xff0c;很多网站都有手机号验证码登录功能 如百度 实现之前咱可以来验证码有啥特点&#xff1a;一定时间内过期、验证码随机、与手机号会唯一匹配 所以可以使用redis的string来实现更容易&#xff0c;k…

Hive on Spark优化

文章目录 第1章集群环境概述1.1 集群配置概述1.2 集群规划概述 第2章 Yarn配置2.1 Yarn配置说明2.2 Yarn配置实操 第3章 Spark配置3.1 Executor配置说明3.1.1 Executor CPU核数配置3.1.2 Executor内存配置3.1.3 Executor个数配置 3.2 Driver配置说明3.3 Spark配置实操 第4章 Hi…