【QT】Qt 网络

news2024/9/29 9:31:58

Qt 网络

  • Qt 网络
    • 1. UDP Socket
      • (1)核心 API 概览
      • (2)回显服务器
      • (3)回显客户端
    • 2. TCP Socket
      • (1)核心 API 概览
      • (2)回显服务器
      • (3)回显客户端

Qt 网络

和多线程类似,Qt 为了⽀持跨平台,对网络编程的 API 也进行了重新封装。

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

1. UDP Socket

(1)核心 API 概览

主要的类有两个:QUdpSocketQNetworkDatagram

QUdpSocket 表示⼀个 UDPsocket 文件。

在这里插入图片描述

QNetworkDatagram 表示⼀个 UDP 数据报

在这里插入图片描述

(2)回显服务器

1、创建界面,包含⼀个 QListWidget 用来显示信息
2、 创建 QUdpSocket 成员,修改 widget.h:

			class Widget : public QWidget
			{
			    Q_OBJECT
			public:
			    Widget(QWidget *parent = nullptr);
			    ~Widget();
			
			private:
			    Ui::Widget *ui;
			    QUdpSocket *socket;
			};

修改 widget.cpp,完成 socket 后续的初始化。⼀般来说, 要先连接信号槽, 再绑定端⼝。如果顺序反过来, 可能会出现端⼝绑定好了之后, 请求就过来了. 此时还没来得及连接信号槽. 那么这个请求就有可能错过了.

			Widget::Widget(QWidget *parent)
			    : QWidget(parent), ui(new Ui::Widget)
			{
			    ui->setupUi(this);
			    // 1. 设置窗⼝标题
			    this->setWindowTitle("服务器");
			    // 2. 实例化 socket
			    socket = new QUdpSocket(this);
			    // 3. 连接信号槽, 处理收到的请求
			    connect(socket, &QUdpSocket::readyRead, this, &Widget::processRequest);
			    // 4. 绑定端⼝
			    bool ret = socket->bind(QHostAddress::Any, 9090);
			    if (!ret)
			    {
			        QMessageBox::critical(nullptr, "服务器启动出错", socket->errorString());
			        return;
			    }
			}

3、实现 processRequest , 完成处理请求的过程

  • 读取请求并解析

  • 根据请求计算响应

  • 把响应写回到客户端

      		void Widget::processRequest()
      		{
      		    // 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;
			}

(3)回显客户端

1、创建界面,包含⼀个 QLineEdit , QPushButton , QListWidget;

  • 先使用⽔平布局把 QLineEditQPushButton 放好, 并设置这两个控件的垂直方向的 sizePolicy 为 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, 定义成员

			class Widget : public QWidget
			{
			    Q_OBJECT
			public:
			    Widget(QWidget *parent = nullptr);
			    ~Widget();
			
			private:
			    Ui::Widget *ui;
			    // 创建 socket 成员
			    QUdpSocket *socket;
			};

修改 widget.cpp, 初始化 socket

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

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("");
			}

5、再次修改 Widget 的构造函数, 通过信号槽, 来处理服务器的响应.

			connect(socket, &QUdpSocket::readyRead, this, [=]()
			{
			    const QNetworkDatagram responseDatagram = socket->receiveDatagram();
			    QString response = responseDatagram.data();
			    ui->listWidget->addItem(QString("服务器说: ") + response); 
			});

2. TCP Socket

(1)核心 API 概览

核⼼类是两个: QTcpServerQTcpSocket

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

在这里插入图片描述

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

在这里插入图片描述

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

例如:

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

(2)回显服务器

1、创建界⾯. 包含⼀个 QListWidget , ⽤于显⽰收到的数据.

在这里插入图片描述

2、创建 QTcpServer 并初始化

修改 widget.h, 添加 QTcpServer 指针成员

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

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

  • 设置窗⼝标题

  • 实例化 TCP server. (⽗元素设为当前控件, 会在⽗元素销毁时被⼀起销毁).

  • 通过信号槽, 处理客⼾端建⽴的新连接.

  • 监听端口

      		Widget::Widget(QWidget *parent)
      		    : QWidget(parent), ui(new Ui::Widget)
      		{
      		    ui->setupUi(this);
      		    // 1. 设置窗⼝标题
      		    this->setWindowTitle("服务器");
      		    // 2. 实例化 TCP server
      		    tcpServer = new QTcpServer(this);
      		
      		    // 3. 通过信号槽, 处理客⼾端建⽴的新连接.
      		    connect(tcpServer, &QTcpServer::newConnection, this,
      		            &Widget::processConnection);
      		
      		    // 4. 监听端⼝
      		    bool ret = tcpServer->listen(QHostAddress::Any, 9090);
      		    if (!ret)
      		    {
      		        QMessageBox::critical(nullptr, "服务器启动失败!", tcpServer - > errorString());
      		        exit(1);
      		    }
      		}
    

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

  • 获取到新的连接对应的 socket.

  • 通过信号槽, 处理收到请求的情况

  • 通过信号槽, 处理断开连接的情况

      		void Widget::processConnection()
      		{
      		    // 1. 获取到新的连接对应的 socket.
      		    QTcpSocket *clientSocket = tcpServer->nextPendingConnection();
      		    QString log = QString("[") + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] 客⼾端上线!";
      		    ui->listWidget->addItem(log);
      		    // 2. 通过信号槽, 处理收到请求的情况
      		    connect(clientSocket, &QTcpSocket::readyRead, this, [=]()
      		    {
      		        // a) 读取请求
      		        QString request = clientSocket->readAll();
      		        // b) 根据请求处理响应
      		        const QString& response = process(request);
      		        // c) 把响应写回客⼾端
      		        clientSocket->write(response.toUtf8());
      		        QString log = QString("[") + clientSocket->peerAddress().toString()
      		        + ":" + QString::number(clientSocket->peerPort()) + "] req: " + 
      		        request + ", resp: " + response;
      		        ui->listWidget->addItem(log); 
      		    });
      		    // 3. 通过信号槽, 处理断开连接的情况
      		    connect(clientSocket, &QTcpSocket::disconnected, this, [=]()
      		    {
      		        QString log = QString("[") + clientSocket->peerAddress().toString()
      		        + ":" + QString::number(clientSocket->peerPort()) + "] 客户端下线!";
      		        ui->listWidget->addItem(log);
      		        // 删除 clientSocket
      		        clientSocket->deleteLater(); 
      		    });
      		}
    

4、实现 process 方法, 实现根据请求处理响应

由于我们此处是实现回显服务器. 所以 process ⽅法中并没有包含实质性的内容

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

(3)回显客户端

1、创建界⾯;包含⼀个 QLineEdit , QPushButton , QListWidget

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

在这里插入图片描述

2、创建 QTcpSocket 并实例化

修改 widget.h, 创建成员.

			class Widget : public QWidget
			{
			    Q_OBJECT
			public:
			    Widget(QWidget *parent = nullptr);
			    ~Widget();
			
			private:
			    Ui::Widget *ui;
			    // 新增 QTcpSocket
			    QTcpSocket *socket;
			};

修改 widget.cpp, 对 QTcpSocket 进行实例化

  • 设置窗⼝标题

  • 实例化 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. 等待并确认连接是否出错.
      		    if (!socket->waitForConnected())
      		    {
      		        QMessageBox::critical(nullptr, "连接服务器出错!", socket->errorString());
      		        exit(1);
      		    }
      		}
    

3、修改 widget.cpp, 给按钮增加点击的 slot 函数, 实现发送请求给服务器.

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

4、修改 widget.cpp 中的 Widget 构造函数, 通过信号槽, 处理收到的服务器的响应.

			// 处理服务器返回的响应.
			connect(socket, &QTcpSocket::readyRead, this, [=]()
			{
			    QString response = socket->readAll();
			    qDebug() << response;
			    ui->listWidget->addItem(QString("服务器说: ") + response); 
			});

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

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

相关文章

【编程笔记】解决移动硬盘无法访问文件或目录损坏且无法读取

解决移动硬盘无法访问文件或目录损坏且无法读取 只解决&#xff1a;移动硬盘无法访问文件或目录损坏且无法读取 问题 由于频繁下载数据&#xff0c;多次安装虚拟机导致磁盘无法被系统识别。磁盘本身是好的&#xff0c;只是不能被识别&#xff0c;如果将磁盘格式化&#xff0c…

Linux 基于 Docker 容器化部署 Pmhub 项目

文章目录 Linux 基于 Docker 容器化部署Pmhub项目前置准备条件( 必做 )MYSQL环境配置( 必做 )Redis环境配置( 必做 )Nacos环境配置( 选做 )Seata环境配置( 选做 )容器可视化工具Portainer 部署各服务到DockerPmHub-gateway修改配置文件bootstrap.yml修改Nacos中pmhub-gateway-d…

英特尔股市暴跌,财报亏损 | HuggingFace 实现盈利 |iOS18 Beta 苹果AI

写在前面 了解一下最近科技圈发生的一些事情 英特尔 硬件巨头英特尔宣布裁掉1.5w个岗位&#xff0c;约占英特尔员工的12%&#xff0c;非常的夸张。本次裁员可能是由于前段时间英特尔的i7&#xff0c;i9的13/14代处理器的暴雷&#xff0c;导致英特尔Q2的财报低迷。 今年以来…

IDC权威认可:亚信安全引跑中国DDI市场

近日&#xff0c;国际数据公司&#xff08;IDC&#xff09;正式发布了《IDC China Semiannual DDI Tracker, 2023H2》&#xff0c;亚信安全域名服务和地址分配及管理系统&#xff08;AIDDI&#xff09;凭借在企业核心网络防护中自动化、安全性、智能化的突出能力&#xff0c;占…

十分钟带你速通 Vue 组件

自定义组件 组件是可复用的 Vue 实例&#xff0c;在开发过程中&#xff0c;我们可以把重复用到的功能封装成自定义组件&#xff0c;达到便捷开发的目的。 组件的组织 通常一个应用会以一棵嵌套的组件树的形式来组织&#xff1a; 你可能会有头部导航、内容区、侧边栏等组件&a…

WEB漏洞-SQL注入之MYSQL注入

跨库注入的原理&#xff1a;针对同一IP下的不同域名 同一服务器下 网站A对应数据库A 网站B对应数据库B 网站C对应数据库C 如果某网站的存在注入点&#xff0c;注入点的权限恰好是root权限&#xff0c;也就是最高权限&#xff0c;那么可以通过跨库注入获取其他网站的数据库…

室内宠物空气净化器哪个好?排名靠前室内宠物空气净化器使用感受

自从家里有了4只英短后&#xff0c;一到季节我就得不停的拖地刷床&#xff0c;除了这些可以手动清理的猫毛之外&#xff0c;那么空气中的猫毛怎么办&#xff1f;感受一下40度高温的养猫人&#xff0c;给掉毛怪疏毛浮毛飘飘&#xff0c;逃不过的饮水机&#xff0c;各个角落&…

YOLOv8部署的4种不同部署方式推理速度对比:pytorch、onnx、ncnn、tflite

1.模型转换 首先,我们将yolov8n.pt转换分别转换成onnx、ncnn、tflite格式模型,供后续使用不同模型部署使用,进行速度对比测试。转换代码如下: # 转onnx yolo export model=yolov8n.pt format=onnx# 转ncnn yolo export model=yolov8n.pt format=ncnn# 转tflite yolo expo…

【C++】windows11环境包管理工具vcpkg配置和教程

【C】windows11环境包管理工具vcpkg配置和教程 文章目录 【C】windows11环境包管理工具vcpkg配置和教程vcpkg 概述Vckpg 下载和安装先决条件下载安装 初识vcpkg&#xff1a;新建helloworld项目集成到 Visual Studio全局集成和移除集成到项目 总结 vcpkg 概述 开源库绝大部分都…

Maven配置国内镜像仓库和本地仓库

参考文章&#xff1a;IDEA配置Maven教程&#xff08;超详细版~)_idea maven配置教程-CSDN博客 1.找到Maven的 settings.xml文件 我的按照路径是&#xff1a;C:\Program Files\Java\apache-maven-3.9.8\conf 2.打开settings.xml文件 我的打开是这个样子 3.增加本地仓库 在根…

简单的docker学习 第12章 Docker Swarm

第12章 Docker Swarm 12.1 swarm 理论基础 12.1.1 简介 Docker Swarm 是由 Docker 公司推出的 Docker 的原生集群管理系统&#xff0c;它将一个 Docker主机池变成了一个单独的虚拟主机&#xff0c;用户只需通过简单的 API 即可实现与 Docker 集群的通信。Docker Swarm 使用 …

护眼大路灯哪个牌子好?2024学生护眼大路灯推荐

护眼大路灯哪个牌子好&#xff1f;护眼大路灯不仅能够提供日常的光线照明&#xff0c;还模拟了太阳光光线&#xff0c;使在室内用眼学习也能够有着自然光般的舒适感&#xff0c;但现在市场上有许多对产品质量把控不过关、光线效果欠佳、存有安全隐患的劣质护眼大路灯产品&#…

MySQL索引及索引的优化策略

1.什么是索引&#xff1f; 索引是对数据库表中一列或多列的值进行排序的一种结构&#xff0c;使用索引可快速访问数据库表中的特定信息 2.为什么使用索引&#xff1a; 1.高效性&#xff1a;利用索引可以提高数据库的查询效率 2.唯一性&#xff1a;索引可以确保所查的数据的唯一…

【已解决】没有密码,如何解除PPT的“只读方式”?

PPT可以设置有密码的“只读方式”&#xff0c;保护文件不被随意编辑更改。 在设置保护后&#xff0c;打开PPT时就会弹出对话框&#xff0c;提示需要“输入密码以修改或以只读方式打开”&#xff0c;也就是输入密码才能编辑修改PPT&#xff0c;如果点击“只读”也能打开文件&am…

2025浙江(杭州)国际安防产品展览会(浙江安博会)

2025浙江&#xff08;杭州&#xff09;国际智慧城市与安防产品展览会 2025hangzhou smart city And Security Expo 时间:2025年4月23-25日 地点:杭州国际博览中心 展会介绍 浙江&#xff08;杭州&#xff09;国际智慧城市及安防产品博览会&#xff08;简称:浙江安博会&#…

探索AutoGGUF:新时代的量化工具

大家好&#xff01;今天我非常激动地向大家介绍一款新工具&#xff0c;叫做AutoGGUF。AutoGGUF是一款全新的图形用户界面&#xff0c;它是用Python语言编写的&#xff0c;基于PyQt6库。PyQt6是Python的一种绑定&#xff0c;用于创建图形用户界面。而AutoGGUF的设计初衷是简化使…

docker 安装 geoserver

docker 安装 geoserver 文章目录 docker 安装 geoserver一、准备工作二、创建容器三、安装插件3.1 插件下载3.2 将插件拷贝进容器3.3 创建新镜像 四、配置 nginx 代理 一、准备工作 # 获取最新镜像 docker pull kartoza/geoserver#创建数据持久化目录 /usr/local/application/…

大模型RAG实战|文本转换:文本分割器、中文标题增强与高级提取管道

ThinkRAG大模型RAG实战系列文章&#xff0c;带你深入探索使用LlamaIndex框架&#xff0c;构建本地大模型知识库问答系统。本系列涵盖知识库管理、检索优化、模型本地部署等主题&#xff0c;通过代码与实例&#xff0c;讲解如何打造生产级系统&#xff0c;实现本地知识库的快速检…

抽象代数精解【8】

文章目录 希尔密码矩阵矩阵基本概念行列式基本概念特殊矩阵关于乘法运算构成群 加解密原理密钥加密函数解密函数 Z 26 上的运算&#xff08; Z 256 与此类似&#xff09; Z_{26}上的运算&#xff08;Z_{256}与此类似&#xff09; Z26​上的运算&#xff08;Z256​与此类似&…

一篇模块化RAG之最新全面系统性综述

RAG访问外部知识库增强了LLMs处理知识密集型任务的能力&#xff0c;随着应用场景需求的增加&#xff0c;RAG系统变得更加复杂。传统的RAG依赖于简单的相似性检索&#xff0c;**面对复杂查询和变化多端的文本块时表现不佳&#xff1a;**对查询的浅层理解、检索冗余和噪声。 朴素…