LanChatRoom局域网聊天室

CPP已经结课,我提交的项目是Qt的入门项目,局域网聊天室LanChatRoom。
image.png
这个代码重构了很多遍。第一遍是照着明哥推荐到书,把代码抄了一遍。
但抄下来之后,各种问题,而且是清朝老代码。抄了一遍之后,对代码的业务逻辑已经有了一个大体的了解。
整个开发周期持续了一周,其实最开始两天就已经能跑了。但我觉得远古代码太丑陋了,所以我扔掉了了书本,选择重写。
重写的过程也是曲折的,而且每次都遇到新的或旧的问题。这些问题以及解决方案将在接下来的内容中分享给大家。希望可以帮助到有需要的同学。

IDE的选择

如果是跟我一样的新手的话,第一遍建议是去找书,抄项目代码。当然是理解地抄,而不是单纯的Ctrl+CV。
IDE建议开始选择Qt自带的QtCreater。因为这涉及到对ui的操作,以及信号槽机制。这对没有qt经验的同学来说很不友好。
但是QtCreater太丑陋了,而且代码补全也不好用。
所以我当时是已经熟悉了ui的各项操作之后,就转到clion里了。
image.png
熟悉信号槽之后,就可以考虑转到clion了。
而且clion默认配置的cmakelist文件也更加清晰。
image.png
我一开始是去书栈网找Qt的教程,但它们很少用到ui文件,而是直接用代码控制元素。实际上很多对象的属性和方法,是不需要去记的,直接用designer编辑ui文件就可以。

消息广播

消息广播利用的是传输层协议UDP。
消息广播需要将消息发送给同一局域网内的所有设备。如果使用TCP协议,则需要在每个设备上都建立连接,这会增加网络开销。而UDP协议是无连接的协议,只需要设置源IP地址、源端口、目标IP地址和目标端口即可发送数据,因此可以提高传输效率。
UDP协议也存在一些缺点,例如数据传输不保证可靠性。在局域网聊天室中,如果某个设备没有接收到消息,则不会影响其他设备的正常使用。
image.png

文件传输

文件传输用的是传输层协议TCP。
TCP具有可靠性、有序性和流量控制等特性,可以保证文件传输的顺利进行。
而且文件的发送也利用了qt的信号槽机制。触发readyread或byteswritten信号之后,才传输下一部分文件。能够正常进入事件循环。这样不会堵塞当前线程,实现类似多线程的效果。
如果用循环的话,会卡在循环内,无法进入事件循环,在传输结束之前,显示“无响应”。
image.png
文件收发有很多共有的部分,比如界面元素、进度条更新。这些共有的部分可以单独封装,交给子类实现。这属于软件设计模式中的策略模式。
QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy);这是我每次重写都遇到的问题,需要指定代理方式,这可能跟我一直开着系统代理有关。

QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy);
connect(ui->selectFileButton, &QPushButton::clicked, this, &FileTransfer::selectFile);
connect(ui->transferFileButton, &QPushButton::clicked, this, &FileTransfer::transferFile);
connect(ui->cancelButton, &QPushButton::clicked, this, [=]() {this->close();
});

connect是qt特有的信号槽方法。使用的话需要继承QObject类,或者他的派生类。
selectFiletransferFile是纯虚函数,具体的策略在子类中实现。这里必须要用纯虚函数,交由子类实现。
cancelButton触发“取消”事件,通过lambda表达式实现。无论是接收还是发送,点击取消按钮的结果,都是关闭窗口,因此选择直接使用lambda表达式简化代码。

文件图标

image.png
我是在Clion中构建的的cmake项目。
image.png
需要在构建目录中添加.rc资源文件,并在.rc资源文件中指定IDI_ICON1 ICON "resources/icons/beer.ico"
后面的路径是相对于构建目录的,如果不确定写相对还是写绝对,可以都试一试。

回车发送消息

实现原理就是重写eventFilter方法。
image.png
如果检测到键盘事件,先判断是不是回车,如果是回车就发送消息,如果是CTRL+回车,就插入换行符。
如果是粘贴事件,就尝试插入图片。插入图片有两种可能:

  • 在粘贴板的元数据中
  • 粘贴板存放的是文件地址url

把这两种情况都尝试一遍,如果能获取到图片,那就插入到输入框。
还创建了一个自定义工具类,实现一个静态工具方法imageToBase64。用于将image对象转换为base64格式的字符串,嵌入到html中。

构建多个可执行文件

一个项目构建多个可执行文件,而不是为每一个可执行文件创建新的项目。
这需要修改CmakeList文件,为每一个构建目标指定文件。
image.png
添加自定义目标add_custom_target,允许一次编译所有可执行文件。
image.png
添加可执行文件add_executable,允许一个项目编译生成多个可执行文件。
image.png
括号内,第一个参数LanChatRoom是构建后的可执行文件名。
后面的所有参数,都是参与构建这个可执行文件的源代码文件,包括头文件、源文件、资源文件。之后可能还会导入更多。

条件编译

每次切换debug和release两种状态的时候,都增删代码,是不现实的。
这样项目中每一处需要修改的地方都需要修改。
在最开始的时候,我就是这么做的。把一些调试信息显示在ui上。比如,本来这个标签是显示文件路径的,我现在显示TcpSocket的错误信息。
前面也提了,这个代码重构了很多遍,每次重构的原因,都包括:这一编写的太丑了,乱七八糟的。
重构很多遍之后,才想起来软件设计师备考时学的:软件设计模式。
这种工科的概念,如果脱离实践,那么只是空洞的文字。就算接触到了,也需要重复很多遍才能把认识和实践联系起来。
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
上面这行代码是写在CmakeList中的,它的意思是,在预处理阶段,添加宏DEBUG
在代码中需要调试的地方,用#ifdef DEBUG,进行条件编译。
image.png

窗口程序,不显示cmd

这需要在CmakeList中添加:

set(CMAKE_WIN32_EXECUTABLE TRUE)

否则会携带一个控制台窗口。

动态链接库

这一部分的作用是在编译时链接动态链接库。
并在编译后,把动态链接库.dll复制到目标目录中。

target_link_libraries(LanChatRoomQt::CoreQt::GuiQt::WidgetsQt::Network
)
target_link_libraries(FileSenderQt::CoreQt::GuiQt::WidgetsQt::Network
)
target_link_libraries(FileReceiverQt::CoreQt::GuiQt::WidgetsQt::Network
)
if (WIN32 AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)set(DEBUG_SUFFIX)if (MSVC AND CMAKE_BUILD_TYPE MATCHES "Debug")set(DEBUG_SUFFIX "d")endif ()set(QT_INSTALL_PATH "${CMAKE_PREFIX_PATH}")if (NOT EXISTS "${QT_INSTALL_PATH}/bin")set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..")if (NOT EXISTS "${QT_INSTALL_PATH}/bin")set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..")endif ()endif ()if (EXISTS "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll")add_custom_command(TARGET ${PROJECT_NAME} POST_BUILDCOMMAND ${CMAKE_COMMAND} -E make_directory"$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/")add_custom_command(TARGET ${PROJECT_NAME} POST_BUILDCOMMAND ${CMAKE_COMMAND} -E copy"${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll""$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/")endif ()foreach (QT_LIB Core Gui Widgets Network)add_custom_command(TARGET ${PROJECT_NAME} POST_BUILDCOMMAND ${CMAKE_COMMAND} -E copy"${QT_INSTALL_PATH}/bin/Qt6${QT_LIB}${DEBUG_SUFFIX}.dll""$<TARGET_FILE_DIR:${PROJECT_NAME}>")endforeach (QT_LIB)
endif ()

实际上,可以只保留target_link_libraries部分。
因为后面一大段的if,作用是导入动态链接库文件,导入的这些仍然是不完整的。
最后需要用windeployqt来补充依赖。用法就是windeployqt [文件名],比如:windeployqt lanchatroom.exe。win环境下是大小写都可以的。
image.png
使用windeployqt需要预先将所在目录添加到环境变量中,以我的电脑为例,windeployqt在目录C:\Tools\Qt\6.6.1\mingw_64\bin下。
也就是Qt版本文件夹下的mingw_64\bin

软件设计模式

我最开始接触,是前段时间准备软考的时候。
重写了这么多编,才对软件设计模式有稍微浅薄的理解。
这里面也用到了策略、状态等模式。
如果没有软件设计模式,那么整个项目将非常混乱。我觉得,从事软件工程,软件设计模式是必须的。

软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。

下面是当时汇报的PPT,对其他组的作品也算是降维打击了,哈哈。
LanChatRoom - yuque.pptx

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

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

相关文章

Python访问ElasticSearch

ElasticSearch是广受欢迎的NoSQL数据库&#xff0c;其分布式架构提供了极佳的数据空间的水平扩展能力&#xff0c;同时保障了数据的可靠性&#xff1b;反向索引技术使得数据检索和查询速度非常快。更多功能参见官网介绍 https://www.elastic.co/cn/elasticsearch/ 下面简单罗列…

python慕课版课后题答案,python慕课作业答案

这篇文章主要介绍了中国大学慕课python测验答案&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获&#xff0c;下面让小编带着大家一起了解一下。 Flag 8月29日前学习完MOOC_Python课程&#xff0c;以下各测试题均来自于Python…

数据结构-线性表-顺序存储

线性表概念 线性表是一种线性结构&#xff0c;它是由n(n≥0)个数据元素a1&#xff0c;a2,…&#xff0c;an组成的有限序列。 线性表的基本运算 初始化 Initiate (L)&#xff1a;建立一个空表 L()&#xff0c;L 不含数据元素。 求表长度 Length(L)&#xff1a;返回线性表 L 的…

深度学习|10.2 边缘检测示例 10.3 更多边缘检测

文章目录 如何在编程中实现卷积运算使用卷积实现边缘检测结果矩阵的元素正负性质的意义水平分类器如何构造卷积运算使用的矩阵 原矩阵通过一个过滤器&#xff08;filter&#xff09;/核心&#xff08;kernel&#xff09;来生成一个新的矩阵。 如何在编程中实现卷积运算 使用卷积…

Kafka安全认证机制详解之SASL_PLAIN

一、概述 官方文档&#xff1a; https://kafka.apache.org/documentation/#security 在官方文档中&#xff0c;kafka有五种加密认证方式&#xff0c;分别如下&#xff1a; SSL&#xff1a;用于测试环境SASL/GSSAPI (Kerberos) &#xff1a;使用kerberos认证&#xff0c;密码是…

Redis:原理+项目实战——Redis实战3(Redis缓存最佳实践(问题解析+高级实现))

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;Redis&#xff1a;原理项目实战——Redis实战2&#xff08;Redis实现短信登录&#xff08;原理剖析代码优化&#xff09;&#x…

golang利用alpn机制实现优先以http2协议访问服务端

文章目录 配置连接池发起请求 配置连接池 初始化http连接池 t1 : &http.Transport{Proxy: func(*http.Request) (*url.URL, error) {// 不读取HTTP_PROXY环境变量return nil, nil},DialContext: (&net.Dialer{// TCP握手超时Timeout: 3 * time.Second,// TCP协议的长…

【计算机毕业设计】SSM游戏点评网站

项目介绍 本项目分为前后台&#xff0c;前台为普通用户登录&#xff0c;后台为管理员登录&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,管理员管理,网站用户管理,游戏资讯管理,游戏类型管理,城市信息管理,竞技场管理,游戏信息管理,游戏评价信息管理等功能。…

【总结】请求跨域及其终极解决方案

文章目录 跨域示例浏览器的同源策略跨域访问类型跨域解决方案方案 1:允许跨域,配置 CORS自定义 Filter 实现使用 Spring 自带的 CorsFilter使用 @CrossOrigin 注解SpringMVC 全局配置Spring Cloud Gateway 配置 CORS方案 2:实现同源请求代理服务器实现同源子域与父域修改(d…

使用UDF扩展Spark SQL

Apache Spark是一个强大的分布式计算框架&#xff0c;Spark SQL是其一个核心模块&#xff0c;用于处理结构化数据。虽然Spark SQL内置了许多强大的函数和操作&#xff0c;但有时可能需要自定义函数来处理特定的数据需求。在Spark SQL中&#xff0c;可以使用UDF&#xff08;User…

视频剪辑技巧:制作视频画中画效果,背景图片的选取与运用

在视频剪辑中&#xff0c;画中画效果是一种常用的技巧&#xff0c;它可以在同一屏幕上展示两个或多个视频片段。这种效果在制作教程、比较两个物品或展示对话等场景中非常实用。现在一起来看看云炫AI智剪如何制作视频画中画效果&#xff0c;选取和运用背景图片的方法。 一起来看…

计算机网络---知识点

ARPANET----NFSNET—ANSNET—Internet发展及协议 移动互联网 物联网 无线自组网、无线传感器网络、无线个域网 ISO/OSI网络体系结构 TCP/IP网络体系结构 对等通信、PDU 电路交换、报文交换、分组报文交换 虚电路、数据报 信道复用技术 网络性能的主要指标&#xff08…

【Apache-2.0】springboot-openai-chatgpt超级AI大脑产品架构图

springboot-openai-chatgpt: 一个基于SpringCloud的Chatgpt机器人&#xff0c;已对接GPT-3.5、GPT-4.0、百度文心一言、stable diffusion AI绘图、Midjourney绘图。用户可以在界面上与聊天机器人进行对话&#xff0c;聊天机器人会根据用户的输入自动生成回复。同时也支持画图&a…

RT-Thread内核移植

目录 前言一、实验平台简介1.1 W601简介1.2 RT-Thread简介1.3 ENV简介 二、开发环境搭建2.1 MDK安装2.2 Git安装2.3 RT-Thread相关下载2.4 其他素材 三、移植RT-Thread四、ENV使用五、W601开发板下载验证5.1 使用串口下载软件5.2 ST-Link下载 前言 本文以正点原子W601开发板为例…

阿里云服务器端口PPTP 1723放行教程

阿里云服务器安装PPTP VPN需要先开通1723端口&#xff0c;阿里云服务器端口是在安全组中操作的&#xff0c;阿里云服务器网aliyunfuwuqi.com来详细说明阿里云服务器安全组开放PPTP VPN专用1723端口教程&#xff1a; 阿里云服务器放行1723端口教程 PPTP是点对点隧道协议&#…

STM32 学习(二)GPIO

目录 一、GPIO 简介 1.1 GPIO 基本结构 1.2 GPIO 位结构 1.3 GPIO 工作模式 二、GPIO 输出 三、GPIO 输入 1.1 传感器模块 1.2 开关 一、GPIO 简介 GPIO&#xff08;General Purpose Input Output&#xff09;即通用输入输出口。 1.1 GPIO 基本结构 如下图&#xff0…

kotlin isEmpty/isNotEmpty/isNullOrEmpty和isBlank/isNotBlank/isNullOrBlank

kotlin 中 isEmpty &#xff1a;如果判断的字符为空返回值返回true否则返回false 它的源码 kotlin.internal.InlineOnly public inline fun CharSequence.isEmpty(): Boolean length 0 length 0: 首先检查字符序列的长度是否为 0。如果长度为 0&#xff0c;则表明这个字…

ClickHouse(20)ClickHouse集成PostgreSQL表引擎详细解析

文章目录 PostgreSQL创建一张表实施细节用法示例 资料分享参考文章 PostgreSQL PostgreSQL 引擎允许 ClickHouse 对存储在远程 PostgreSQL 服务器上的数据执行 SELECT 和 INSERT 查询. 创建一张表 CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] (name…

C#使用 OpenHardwareMonitor获取CPU或显卡温度、使用率、时钟频率相关方式

C# 去获取电脑相关的基础信息&#xff0c;还是需要借助 外部的库&#xff0c;我这边尝试了自己去实现它 网上有一些信息&#xff0c;但不太完整&#xff0c;都比较零碎&#xff0c;这边尽量将代码完整的去展示出来 OpenHardwareMonitor获取CPU的温度和频率需要管理员权限 在没…

回顾2023,展望2024

又是一年起始&#xff0c;在这里回顾一下过去&#xff0c;展望一下未来。今年已经是自己的本命年了&#xff0c;龙行龘龘&#xff0c;希望在自己的本位年能够有更多的努力和更多的收获。 关于工作 在无锡工作已经一个完整的年份了&#xff0c;工作上平平稳稳&#xff0c;也适应…