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.hqwc.cn/news/315941.html

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

相关文章

二叉树的前序遍历 、二叉树的最大深度、平衡二叉树、二叉树遍历【LeetCode刷题日志】

目录 一、二叉树的前序遍历 方法一&#xff1a;全局变量记录节点个数 方法二&#xff1a;传址调用记录节点个数 二、二叉树的最大深度 三、平衡二叉树 四、二叉树遍历 一、二叉树的前序遍历 方法一&#xff1a;全局变量记录节点个数 计算树的节点数: 函数TreeSize用于…

IIS通过ARR实现负载均衡

一、实现整体方式介绍 项目中部署在windows服务器上的项目,需要部署负载均衡,本来想用nginx来配置的,奈何iis上有几个项目,把80端口和443端口占用了,nginx就用不了了(因为通过域名访问的,必须要用80和443端口),只能通过IIS的方式实现了。 这里用2个服务在一台机器上…

Flutter 混合开发 - 动态下发 libflutter.so libapp.so

背景 最近在做包体积优化&#xff0c;在完成代码混淆、压缩&#xff0c;裁剪ndk支持架构&#xff0c;以及资源压缩&#xff08;如图片转webp、mp3压缩等&#xff09;后发现安装包的中占比较大的仍是 so 动态库依赖。 具体查看发现 libflutter.so 和 libapp.so 的体积是最大的&…

nccl 源码安装与应用示例 附源码

1&#xff0c; 官方下载网址 注意&#xff0c;本文并不使用nv预编译的包来安装&#xff0c;仅供参考&#xff1a; NVIDIA Collective Communications Library (NCCL) | NVIDIA Developer 2&#xff0c;github网址 这里是nv开源的nccl源代码&#xff0c;功能完整&#xff0c;不…

侯捷C++ 2.0 新特性

关键字 nullptr and std::nullptr_t auto 一致性初始化&#xff1a;Uniform Initialization 11之前&#xff0c;初始化方法包括&#xff1a;小括号、大括号、赋值号&#xff0c;这让人困惑。基于这个原因&#xff0c;给他来个统一&#xff0c;即&#xff0c;任何初始化都能够…

C#编程-使用条件构造

使用条件构造 作判定是人的基本能力。判定也是可收编进程序。这有助于确定程序执行指令的顺序。 您可用条件构造来控制程序的流程。条件构造允许您基于被求职的表达式的结果来执行选定语句。 可以包含在C#程序中的各种条件构造是: if…else 构造switch…case 构造if…else构…

git 如何撤销历史某次merge

git&#xff0c;如何 撤销某一次历史提交或merge&#xff0c;并保留该版本的后续提交&#xff1f; 场景1&#xff1a; 你有两个功能迭代版本的分支&#xff0c;一个是 15 号上线&#xff0c;一个是25号上线。5号的时候产品突然说&#xff0c;这两个版本一起上&#xff0c;然后…

【LabVIEW FPGA入门】创建第一个LabVIEW FPGA程序

本教程仅以compactRIO&#xff08;FPGA-RT&#xff09;举例 1.系统配置 1.1软件安装 FPGA-RT 1. LabVIEW Development System (Full or Professional) 2. LabVIEW Real-Time Module 3. LabVIEW FPGA Module 4. NI-RIO drivers 1.2硬件配置 1.使用线缆连接CompactRIO至主机…

软件测试/测试开发丨Pytest结合数据驱动

安装yaml pip install pyyaml pytest结合数据驱动yaml 工程目录结构 数据准备 读取excel文件 openpyxl库的安装 openpyxl库的操作 pytest结合csv实现数据驱动 csv文件介绍 pytest结合json实现数据驱动 最后感谢每一个认真阅读我文章的人&#xff0c;礼尚往来总是要有的&…

Jmeter实现分布式并发

Jmeter实现分布式并发&#xff0c;即使用远程机执行用例。 环境&#xff1a; VMware Fusion Windows系统是win7。 操作过程 1、Master在jmeter.properties添加remote_hosts 2、Slave在jmeter.properties添加server_port 同时把remote_hosts修改为和主机&#xff08;Master…

matlab生成列是0-255渐变的图像

图像大小&#xff1a;640512 8位灰度图 %% 生成图像 %大小&#xff1a;640*512 %类型&#xff1a;灰度图 %灰度值&#xff1a;列按照0-255渐变&#xff0c;故命名为column shade。 clc,clear all,close all; %输入的图 imadouble(imread(lenna2.bmp));%原图 imargb2gray(ima)…

ubuntu系统没有网络图标的解决办法

参考文章:https://blog.csdn.net/qq_56922632/article/details/132309643 1. 执行关闭网络服务的命令&#xff0c;关闭网络服务sudo service NetworkManager stop2. 删除网络的状态文件sudo rm /var/lib/NetworkManager/NetworkManager.state3. 修改网络的配置文件sudo vi /etc…