Qt-系统网络TCP回显客户端与服务端(65)

目录

描述

函数

使用

服务器

准备工作

声明相关函数与对象

绑定并监听

定义槽函数与连接

瑕疵

释放

客户端

准备工作

声明相关函数与对象

初始化并连接服务器

给发送添加槽函数

连接信号槽处理响应函数

测试运行

补充

代码

客户端

服务器


描述

有UDP,自然少不了TCP,不过TCP稍微要复杂一点点

使用 Qt 内部封装好的类去实现一个 TCP 的网络程序

函数

核⼼类是两个:  QTcpServer  和  QTcpSocket

QTcpServer ⽤于监听端⼝, 和获取客⼾端连接

名称类型说明对标原⽣ API
listen(const QHostAddress&,
quint16 port)
⽅法绑定指定的地址和端⼝号,并开始监听.bind 和 listen
nextPendingConnection()⽅法从系统中获取到⼀个已经建⽴好的 tcp 连接.
返回⼀个  QTcpSocket , 表⽰这个客⼾端的连接.
通过这个 socket 对象完成和客⼾端之间的通信.
accept
newConnection 信号有新的客⼾端建⽴连接好之后触发.⽆ (但是类似于 IO 多路复⽤中的通知机制)

QTcpSocket  ⽤⼾客⼾端和服务器之间的数据交互. 

名称类型说明对标原⽣ API
readAll()⽅法

读取当前接收缓冲区中的所有数据.

返回 QByteArray 对象.

read
write(const QByteArray&)⽅法把数据写⼊ socket 中.write
deleteLater⽅法暂时把 socket 对象标记为⽆效. Qt 会在下个事件循环中析构释放该对象.⽆ (但是类似于 "半⾃动化的垃圾回收")
readyRead信号有数据到达并准备就绪时触发.⽆ (但是类似于 IO 多路复⽤中的通知机制)
disconnected信号连接断开时触发.⽆ (但是类似于 IO 多路复⽤中的通知机制)

使用

和UDP一样,我们创建一个回显服务器和客户端,界面设置如下

服务器

准备工作

加上 network

界面设置

声明相关函数与对象

头文件包含,并定义好 槽函数和server指针

绑定并监听

定义槽函数与连接

对端的地址和端口是相对的,这一点在客户端也是如此,服务端的对端就是客户端 

因为信号槽的机制,所以我们并不需要循环等待

只需要等待信号的触发就可以了 

瑕疵

这里的书写是不严谨的,在 TCP 中,由于数据的传输是面向字节流的,所以肯定有字节流相关的问题需要解决,在这里的代码中,我们并没有进行 应用层 上的处理数据,没有定义协议,所以关于数据粘包问题是不能够处理的

释放

和UDP不一样的是,这里我们需要手动释放资源,一个客户端连接对应一个 clientSocket,对应一个文件描述符,而文件描述符则是有限资源,容易因为泄漏问题导致资源紧张,甚至崩溃

 

关于释放,也要十分的小心,要务必保证释放是槽函数的最后一步进行的,这样才不容易造成内存泄漏和文件描述符泄漏

一方面因为 C++ 中并没有垃圾回收机制,Qt 显然也是没有的,但是 Qt,为了应对这方面的问题,引入了一个 半自动回收的函数,deleteLater,延后回收,这一点就能让资源回收,并且不会影响现有槽函数

客户端

准备工作

界面设置

和 udp 界面设置一样,调整好比例,和拉伸系数即可

声明相关函数与对象

初始化并连接服务器

这里和在 Linux 里有点不一样,Linux里面是阻塞式的连接(简单一些),而 Qt 则是封装成了非阻塞式的连接(速度更快,因为可以处理其他的事件),因为 三次握手 其实是需要消耗一定的时间的

给发送添加槽函数

连接信号槽处理响应函数

测试运行

单个客户端

断开连接 

多个客户端

注意要找到 exe 可执行程序,在客户端对应的 build 文件夹中

连接测试运行正常

断开连接测试正常

补充

TCP没有必须要求使用多线程去完成

在很久之前的 Linux 之中的回显服务器的书写是用到了双层循环 

当然了一个 TCP 的服务器一般是不会使用 QT 来写的 

代码

客户端

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* socket;
};
#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);// 1.设置窗口标题this->setWindowTitle("客户端");// 2.创建 socket 对象的实例socket = new QTcpSocket(this);// 3.和服务器建立连接socket->connectToHost("127.0.0.1", 9090);// 4. 连接信号槽,处理响应connect(socket, &QTcpSocket::readyRead, this, [=](){// a)读取出响应数据QString response = socket->readAll();// b)把响应内容显示到界面上ui->listWidget->addItem("服务器说: " + response);});// 5.等待连接建立的结果,确认是否连接成功bool ret = socket->waitForConnected();if(!ret){QMessageBox::critical(this, "连接服务器失败", socket->errorString());exit(1);}
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{// 1.获取到输入框中的内容const QString& text = ui->lineEdit->text();// 2.发送数据给服务器socket->write(text.toUtf8());// 3.把发的消息显示到界面上ui->listWidget->addItem("客户端说: " + text);// 4.清空输入框的内容ui->lineEdit->setText("");
}

服务器

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();private:Ui::Widget *ui;QTcpServer* tcpServer;QString process(const QString request);
};
#endif // WIDGET_H

Widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QTcpSocket>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 1.修改窗口标题this->setWindowTitle("服务器");// 2.创建 QTcpServer 的实例tcpServer = new QTcpServer(this);// 3.通过信号槽,指定如何处理函数connect(tcpServer, &QTcpServer::newConnection, this, &Widget::processConnection);// 4.绑定并监听端口号,一定要在准备工作做完了(连接信号槽),才能进行绑定监听bool ret = tcpServer->listen(QHostAddress::Any, 9090);if(!ret){QMessageBox::critical(this, "服务器启动失败", tcpServer->errorString());exit(1);}
}Widget::~Widget()
{delete ui;
}void Widget::processConnection()
{// 1.通过 tcpServer 拿到一个 socket 对象,通过这个对象来和客户端进行通信QTcpSocket* clientSocket = tcpServer->nextPendingConnection();QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] 客户端上线!";ui->listWidget->addItem(log);// 2.通过信号槽,来处理客户端发来的请求情况connect(clientSocket, &QTcpSocket::readyRead, this, [=](){// a)读取请求数据,此处 readAll 返回的是 QByteArray, 通过赋值转成 QStringQString request = clientSocket->readAll();// b)根据请求处理响应const QString& response = process(request);// c)把响应写回到客户端clientSocket->write(response.toUtf8());// d)把上述信息记录到日志中QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "]"+ " req: " + request + ", resp: " + response;ui->listWidget->addItem(log);});// 3.通过信号槽,来处理客户端断开连接的情况connect(clientSocket, &QTcpSocket::disconnected, this, [=](){// a)把断开连接的信息通过日志显示出来QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] 客户端下线!";ui->listWidget->addItem(log);// b)手动释放 clientSocket 直接使用 delete 是下策,使用 deleteLater 更加合适// delete clientSocket;clientSocket->deleteLater();});
}QString Widget::process(const QString request)
{return request;
}

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

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

相关文章

MySQL 删除数据库

1.使用命令行删除一个数据库 1.1 首先登陆进入 MySQL 操作界面&#xff0c;命令如下&#xff1a; 命令 : mysql -utest -p;1.2 登陆成功之后可以使用如下命令查看当前已有数据库&#xff1a; 命令 : SHOW DATABASES; 执行结果如下图: 如图所示当前已包含 MySQL 系统数据库和…

TensorRT-LLM七日谈 Day3

今天主要是结合理论进一步熟悉TensorRT-LLM的内容 从下面的分享可以看出&#xff0c;TensorRT-LLM是在TensorRT的基础上进行了进一步封装&#xff0c;提供拼batch&#xff0c;量化等推理加速实现方式。 下面的图片更好的展示了TensorRT-LLM的流程&#xff0c;包含权重转换&…

iPhone 16 Pro 拆解揭秘:设计改进与维修便利性

苹果最新推出的iPhone 16系列在许多方面都进行了更新和改进&#xff0c;而这次我们要聚焦的是其中的高端型号——iPhone 16 Pro。 这款手机不仅在性能上有所提升&#xff0c;在内部构造上也带来了不少变化&#xff0c;让我们一起来看看这些细节吧。 更容易进入的内部结构 对于…

一、Java基础

韩顺平Java基础 浮点型使用细节基本数据类型转换自动类型转换强制类型转换 浮点型使用细节 double d 8.1 / 3 的结果是一个非常接近2.7的小数&#xff0c;比如2.69999997&#xff0c;这是计算机的运算规则造成的 基本数据类型转换 自动类型转换 对于第四点&#xff0c;如下…

电脑知识:适用于 Windows 10 的 PDF 编辑器列表

PDF 是一种流行的、多功能且安全的文件格式&#xff0c;用于在线共享文档。但是&#xff0c;如果没有合适的应用程序&#xff0c;查看和编辑 PDF 文件可能会变得复杂。 幸运的是&#xff0c;有很多 PDF 编辑器可以帮助您更正重要文档上的错误、填写表格、为合同添加签名、更改…

Unity3d折叠Inspector中的变量

InspectorFoldoutGroup插件 [Pixeye.Unity.Foldout("【曲线图】")] public BrokenLineUpDownGraph aimStabilityGraph;[Pixeye.Unity.Foldout("【曲线图】")] public BrokenLineUpGraph aimDensityGraph;[Pixeye.Unity.Foldout("【曲线图】")] p…

MedMamba代码解释及用于糖尿病视网膜病变分类

MedMamba原理和用于糖尿病视网膜病变检测尝试 1.MedMamba原理 MedMamba发表于2024.9.28&#xff0c;是构建在Vision Mamba基础之上&#xff0c;融合了卷积神经网的架构&#xff0c;结构如下图&#xff1a; 原理简述就是图片输入后按通道输入后切分为两部分&#xff0c;一部分走…

Spring Boot 应用开发:入门与实战

Spring Boot 应用开发&#xff1a;入门与实战 引言 Spring Boot 是 Spring 框架的一个子项目&#xff0c;旨在简化 Spring 应用的配置和开发。它通过自动配置和嵌入式服务器&#xff0c;极大地简化了 Java 企业级应用的开发。本文将详细介绍 Spring Boot 的核心概念&#xff…

JVM进阶调优系列(1)类加载器原理一文讲透

今天开始写JVM调优系列&#xff0c;并发编程系列也会继续穿插连载&#xff0c;让各位同学闲暇之余有更多阅读选择。 起笔写第一篇&#xff0c;并不好写。首先要构思整个系列的大概框架&#xff0c;一个好的框架一定是深度上由浅入深、逻辑上有严格顺序&#xff0c;读者订阅跟踪…

《OpenCV计算机视觉》—— 人脸检测

文章目录 一、人脸检测流程介绍二、用于人脸检测的关键方法1.加载分类器&#xff08;cv2.CascadeClassifier()&#xff09;2.检测图像中的人脸&#xff08;cv2.CascadeClassifier.detectMultiscale()&#xff09; 三、代码实现 一、人脸检测流程介绍 下面是一张含有多个人脸的…

使用camunda的DMN实现班级决策案例

班级决策 Camunda 支持DMN1.3版本&#xff0c;在BPMN业务活动流程中&#xff0c;可通过业务规则任务调用DMN决策。DMN决策目的是想把业务代码和决策进行解耦&#xff0c;使决策分析人员只需关心决策即可。 需求描述 通过幼儿园学生年龄age和身高height分配不同的班级&#xff0…

10.13论文阅读

通过联合学习检测和描述关键点增强可变形局部特征 摘要 局部特征提取是计算机视觉中处理图像匹配和检索等关键任务的常用方法。大多数方法的核心理念是图像经历仿射变换&#xff0c;忽略了诸如非刚性形变等更复杂的效果。此外&#xff0c;针对非刚性对应的新兴工作仍然依赖于…

个性化图像生成新王炸!无需微调,Meta重磅发布Imagine yourself:三大核心全面SOTA!

论文链接&#xff1a;https://arxiv.org/pdf/2409.13346 亮点直击 本文提出了“Imagine Yourself”&#xff0c;这是一种用于个性化图像生成的创新型最先进模型。该模型可以将任意参考图像作为输入进行定制化图像生成&#xff0c;并且不需要针对每个对象进行调整。“Imagine Yo…

springboot汽车售票系统演-毕业设计源码07891

基于springboot的汽车售票系统 摘 要 汽车售票系统主要功能模块包括系统用户管理、车次车票信息、车票预定、退票信息、改签信息等&#xff0c;采取面对对象的开发模式进行软件的开发和硬体的架设&#xff0c;能很好的满足实际使用的需求&#xff0c;完善了对应的软体架设以及…

【C】C语言常见概念~

C语言常见概念 转义字符 转义字符&#xff0c;顾名思义&#xff0c;转变原来意思的字符 比如 #include <stdio.h> int main() {printf("abcndef");return 0; }输出的结果为&#xff1a; 将代码修改一下&#xff1a; #include <stdio.h> int main(…

萱仔求职复习系列——2 Linux的常用方法(包含基础进阶高级操作)

由于最近接了一个笔试&#xff0c;发现笔试可能涉及到Linux&#xff0c;我准备临时抱佛脚一下赶紧复习一下Linux的用法哈哈。Linux 的基础用法包含文件系统操作、权限管理、网络配置、进程管理等基本命令&#xff1b;进阶操作包括网络调试、包管理、服务管理和用户管理等&#…

UE5学习笔记24-添加武器弹药

一、给角色的武器添加弹药 1.创建界面&#xff0c;根据笔记23的界面中添加 2.绑定界面控件 UPROPERTY(meta (Bindwidget))UTextBlock* WeaponAmmoAmount;UPROPERTY(meta (Bindwidget))UTextBlock* CarriedAmmoAmount; 3.添加武器类型枚举 3.1创建武器类型枚举头文件 3.2创建文…

【论文解读系列】EdgeNAT: 高效边缘检测的 Transformer

代码&#xff1a; https://github.com/jhjie/edgenat 论文&#xff1a; https://arxiv.org/abs/2408.10527v1 论文 EdgeNAT: Transformer for Efficient Edge Detection 介绍了一种名为EdgeNAT的基于Transformer的边缘检测方法。 1. 背景与动机 EdgeNAT预测结果示例。(a, b)…

软考《信息系统运行管理员》- 4.1信息系统软件运维概述

4.1信息系统软件运维概述 文章目录 4.1信息系统软件运维概述信息系统软件运维的概念信息系统软件的可维护性及维护类型对软件可维护性的度量可以从以下几个方面进行&#xff1a;软件维护分类&#xff1a; 信息系统软件运维的体系1.**需求驱动**2.**运维流程**3.**运维过程**4.*…

LabVIEW提高开发效率技巧----事件触发模式

事件触发模式在LabVIEW开发中是一种常见且有效的编程方法&#xff0c;适用于需要动态响应外部或内部信号的场景。通过事件结构&#xff08;Event Structure&#xff09;和用户自定义事件&#xff08;User Events&#xff09;&#xff0c;开发者可以设计出高效的事件驱动程序&am…