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(LanChatRoom
        Qt::Core
        Qt::Gui
        Qt::Widgets
        Qt::Network
)
target_link_libraries(FileSender
        Qt::Core
        Qt::Gui
        Qt::Widgets
        Qt::Network
)
target_link_libraries(FileReceiver
        Qt::Core
        Qt::Gui
        Qt::Widgets
        Qt::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_BUILD
                COMMAND ${CMAKE_COMMAND} -E make_directory
                "$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/")
        add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
                COMMAND ${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_BUILD
                COMMAND ${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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习 Day23——J3DenseNet算法实战与解析

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制&#x1f680; 文章来源&#xff1a;K同学的学习圈子 文章目录 前言1 我的环境2 pytorch实现DenseNet算法2.1 前期准备2.1.1 引入库2.1.2 设…

JavaScript中alert、prompt 和 confirm区别及使用【通俗易懂】

✨前言✨   本篇文章主要在于&#xff0c;让我们看几个与用户交互的函数&#xff1a;alert&#xff0c;prompt 和confirm的使用及区别 &#x1f352;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f352;博主将持续更新学习记录收获&…

HarmonyOS页面和自定义组件生命周期

页面和自定义组件生命周期 在开始之前&#xff0c;我们先明确自定义组件和页面的关系&#xff1a; 自定义组件&#xff1a;Component装饰的UI单元&#xff0c;可以组合多个系统组件实现UI的复用。页面&#xff1a;即应用的UI页面。可以由一个或者多个自定义组件组成&#xff…

【算法每日一练]-数论 (保姆级教程 篇2 )#行列式 #甜甜花研究 #约数个数 #模数 #数树 #盒子与球

目录 今日知识点&#xff1a; 辗转相减法化下三角求行列式 组合数动态规划打表 约数个数等于质因数的次方1的乘积 求一个模数 将n个不同的球放入r个不同的盒子&#xff1a;f[i][j]f[i-1][j-1]f[i-1][j]*j 行列式 甜甜花的研究 约数个数 模数 数树 盒子与球 行列…

LinkedList与ArrayList的比较

1.LinkedList 基于双向链表&#xff0c;无需连续内存 随机访问慢&#xff08;要沿着链表遍历&#xff09; 头尾插入删除性能高 占用内存多 2.ArrayList 基于数组&#xff0c;需要连续内存 随机访问快&#xff08;指根据下标访问&#xff09; 尾部插入、删除性能可以&…

西门子宣布SIMATIC S-300停产?找找理想替代品——钡铼BL30x系列工控机

1994年&#xff0c;西门子发布了S7系列的第一批产品&#xff0c;其中包括S7-300。SIMATIC S7的推出也见证了新的现场总线标准Profibus的发布&#xff0c;以及率先使用工业总线来促进自动化设备之间的通信。S7-300 CPU系列的巨大成功也帮助西门子进一步巩固了其全球自动化技术领…

格局初现:京东阿里都瞄准了这个万亿级的大市场

核 心 要 点 ▪ 企业采购有哪些痛点和解决方案&#xff1f;行业的关键赛点是什么&#xff1f; ▪ 现行格局是何情况&#xff1f;代表性玩家各自有何特点&#xff1f; ▪ 未来企业采购将往何处去&#xff1f; 当这样一组数据摆在眼前的时候&#xff0c;你或许会感到难以置…

Gitee

Gitee码云 0. 笔记说明1. Gitee概述2. Gitee和GitHub3. 创建Git远程仓库4. 分享已有项目到Gitee5. 文件恢复和合并6. 文件push或pull冲突7. 添加项目成员 0. 笔记说明 该笔记以IDEA 2023专业版进行操作需提前注册好个人gitee账号安装好IDEA的相关gitee插件或者安装Git Bash软件…

C语言:二分查找查找有序数组中的元素

前言 在我们学习C语言的过程中&#xff0c;如果要查找一个数组当中是否存在某一个元素&#xff0c;我们可能会遍历整个数组&#xff0c;来依次判断是否存在这个函数&#xff0c;但这么做是效率极低的&#xff0c;如果数组中有很多个元素&#xff0c;那么我们要查找半天 二分查…

【CASS精品教程】CASS11计算城镇建筑密度

CASS中可以很方便计算建筑密度。 文章目录 一、建筑密度介绍二、CASS计算建筑密度1. 绘制宗地范围2. 绘制建筑物3. 计算建筑密度三、注意事项一、建筑密度介绍 建筑密度(building density;building coverage ratio),指在一定范围内,建筑物的基底面积总和与占用地面积的比…

CTFshow web入门web128-php特性31

开启环境: 一个新的姿势&#xff0c;当php扩展目录下有php_gettext.dll时&#xff1a; _()是一个函数。 _()gettext() 是gettext()的拓展函数&#xff0c;开启text扩展get_defined_vars — 返回由所有已定义变量所组成的数组。 call_user_func — 把第一个参数作为回调函数调…

JAVAEE初阶相关内容第二十弹--HTTP协议

写在前&#xff1a;2024年啦&#xff01;新的一年要努力学习啦 本篇博客围绕HTTP协议&#xff0c;对HTTP协议进行了解&#xff0c;需要理解其工作过程&#xff0c;对HTTP协议格式要清楚&#xff0c;通过抓包工具进行协议分析&#xff0c;认识“方法”、“请求报头”&#xff0c…

阿里云迁移AWS视频点播技术攻坚

文章目录 &#x1f437; 背景&#x1f9a5; 简述&#x1f425; Aws服务&#x1f99c; AWS CloudFormation&#x1f41e; 问题&#x1f409; 落地方案&#x1f989; Aws vs Aliyun&#x1f344; 避坑指南 &#x1f437; 背景 由于AWS整体成本略低于阿里云&#xff0c;公司决定将…

一个项目的上线全过程

营业执照 个体户&#xff08;700执照、2年财务代理 4000&#xff09; 一张一类卡&#xff0c;转卡专用&#xff08;个人卡&#xff09; 120w 45%企业资质&#xff08;个体户&#xff09; 企业微信&#xff08;300元/年、公众号、小程序&#xff09; 相关手续 备案域名去申请支付…

使用UDF扩展Spark SQL

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

C++初阶——类与对象

目录 C宏函数 在使用宏函数时&#xff0c;有几个常见的错误需要注意&#xff1a; 宏函数在某些情况下有以下优势&#xff1a; 1.C宏函数 在 C 中&#xff0c;宏函数&#xff08;Macro Function&#xff09;是使用预处理器定义的宏&#xff08;Macro&#xff09;&#xff0…

Android studio ViewPager2应用设计

一、ViewPager2应用场景&#xff1a; ViewPager2是一个功能强大的滑动容器&#xff0c;提供灵活的页面切换和布局定制功能&#xff0c;使得应用程序界面更加丰富和交互性强&#xff0c;主要应用于以下场景&#xff1a; 1&#xff09;、实现引导页或欢迎页&#xff1a;ViewPag…

Microsoft Visual Studio 2022 install Project 下载慢

1. 关闭Internet 协议版本6 2. 如果没有效果&#xff0c;打开Internet 协议版本4&#xff0c;更改DNS 3. 在浏览器中下载后安装&#xff0c;下载地址如下&#xff1a; Microsoft Visual Studio Installer Projects 2022 - Visual Studio Marketplace 4. 安装时注意关闭vs&…

Maven 整理(含面试题)

Maven是Java 项目必备&#xff0c;Maven 主要服务于基于Java平台的项目构建、依赖管理和项目信息管理。项目构建工具 、更好的管理依赖 目录 Maven安装与运行maven 重要知识点面试题 Maven安装与运行 Maven 是一个项目管理工具&#xff0c;可以对 Java 项目进行构建、依赖管理…

ThreadLocal线程重用导致用户信息错乱的 Bug

在生产上遇到一个诡异的问题&#xff0c;有时获取到的用户信息是别人的。查看代码后&#xff0c;我发现他使用了 ThreadLocal 来缓存获取到的用户信息。 我们知道&#xff0c;ThreadLocal 适用于变量在线程间隔离&#xff0c;而在方法或类间共享的场景。如果用户信息的获取比较…