Qt5开发及实例V2.0-第十八章-Qt-MyselfQQ实例
Qt5开发及实例V2.0-第十八章-Qt-MyselfQQ实例
- 第18章-Qt MyselfQQ
- 18.1 概述
- 18.2 、发送文件
- 18.3 、接收文件
- 18.4 、保证传输的安全和稳定
- 18.5 、总结
- 本章相关例程源码下载
- 1.Qt5开发及实例_CH1801.rar 下载
第18章-Qt MyselfQQ
18.1 概述
MyselfQQ是一个基于Qt5框架开发的轻量级即时通讯软件,支持文本、图片、语音、文件等多种消息类型的发送和接收。其中,文件传输是MyselfQQ的一个核心功能,可以实现高效、稳定、安全的文件传输。
本文将介绍MyselfQQ的文件传输功能,包括如何发送和接收文件,以及如何保证传输的安全性和稳定性。同时,本文会提供相关的代码示例,以方便开发者进行参考和实践。
18.2 、发送文件
1.1 选择文件
要发送文件,需要先选择要发送的文件。在MyselfQQ中,可以通过打开本地文件夹或者拖拽文件到发送窗口来选择要发送的文件。
打开本地文件夹的方法比较简单,只需要在主界面点击“文件”菜单,选择“打开文件夹”,然后在弹出的文件选择框中选择要发送的文件即可。
如果要通过拖拽文件来选择要发送的文件,可以在主界面直接拖拽文件到发送窗口,或者在本地文件夹中选择要发送的文件,然后拖拽到发送窗口即可。
1.2 发送文件
选择好要发送的文件后,就可以将文件发送给对方了。在MyselfQQ中,发送文件的逻辑可以分为以下几个步骤:
- 创建文件传输对象
在发送文件之前,需要先创建文件传输对象。文件传输对象包含了发送方和接收方的相关信息,以及要发送的文件的路径、大小等信息。
文件传输对象的定义如下:
struct FileTransferObject
{QString fileName; // 文件名QString filePath; // 文件路径qint64 fileSize; // 文件大小QString senderName; // 发送方用户名QString receiverName; // 接收方用户名QString ip; // 接收方IP地址qint16 port; // 接收方端口号bool isAccepted; // 是否被接收bool isSending; // 是否正在发送bool isFinished; // 是否发送完成qint64 sentSize; // 已发送大小QTcpSocket* socket; // 用于发送数据的TCP连接
};
其中,senderName和receiverName分别表示发送方和接收方的用户名;ip和port表示接收方的IP地址和端口号;isAccepted、isSending和isFinished分别表示文件是否被接收、是否正在发送、是否发送完成;sentSize表示已发送的文件大小;socket表示用于发送数据的TCP连接。
- 获取接收方IP地址和端口号
在创建文件传输对象之后,需要获取接收方的IP地址和端口号。这里使用UDP广播来实现,即发送一个UDP广播,让接收方返回自己的IP地址和端口号。
发送UDP广播的代码如下:
void MainWindow::broadcast()
{QByteArray datagram = "hello";QHostAddress broadcastAddress = QHostAddress::Broadcast;quint16 port = 6666;udpSocket->writeDatagram(datagram, broadcastAddress, port);
}
在发送UDP广播之后,需要监听接收方返回的IP地址和端口号。当接收到回复消息时,就可以获取到接收方的IP地址和端口号了。
void MainWindow::processPendingDatagrams()
{while (udpSocket->hasPendingDatagrams()){QByteArray datagram;datagram.resize(udpSocket->pendingDatagramSize());QHostAddress sender;quint16 senderPort;udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);// 处理接收到的UDP数据包}
}
处理接收到的UDP数据包的具体代码如下:
void MainWindow::processBroadcastDatagram(const QByteArray& datagram, const QHostAddress& sender, quint16 senderPort)
{if (datagram == "hello"){QByteArray response = QString("%1,%2").arg(QHostInfo::localHostName()).arg(tcpServer->serverPort()).toUtf8();udpSocket->writeDatagram(response, sender, senderPort);}else if (datagram.startsWith("IP:")){QString receiverName = datagram.mid(3);QString ip = sender.toString();quint16 port = senderPort;FileTransferObject* obj = findFileTransferObject(receiverName);if (obj != nullptr){obj->ip = ip;obj->port = port;// 开始发送文件startSendFile(obj);}}
}
在处理接收到的UDP数据包时,如果收到的是“hello”消息,就会回复一个包含本机主机名和TCP监听端口号的消息。如果收到的是“IP:xxx”消息,就会获取到接收方的IP地址和端口号,并开始发送文件。
- 开始发送文件
在获取到接收方的IP地址和端口号后,就可以开始发送文件了。文件的发送采用TCP连接来进行,因为TCP连接可以保证传输的稳定性和安全性。
发送文件的关键代码如下:
void MainWindow::startSendFile(FileTransferObject* obj)
{QTcpSocket* socket = new QTcpSocket(this);obj->socket = socket;connect(socket, SIGNAL(connected()), this, SLOT(onSendConnected()));connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(onBytesWritten(qint64)));connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSendError(QAbstractSocket::SocketError)));socket->connectToHost(obj->ip, obj->port);obj->isSending = true;obj->isAccepted = true;obj->sentSize = 0;obj->isFinished = false;
}void MainWindow::onSendConnected()
{QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());if (socket != nullptr){FileTransferObject* obj = findFileTransferObject(socket);if (obj != nullptr){QFile file(obj->filePath);if (file.open(QIODevice::ReadOnly)){QByteArray block;QDataStream out(&block, QIODevice::WriteOnly);out.setVersion(QDataStream::Qt_5_9);out << qint64(0) << qint64(0) << obj->fileName;qint64 fileSize = file.size();out << fileSize;socket->write(block);obj->isAccepted = true;obj->fileSize = fileSize;}else{socket->close();}}}
}void MainWindow::onBytesWritten(qint64 bytes)
{QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());if (socket != nullptr){FileTransferObject* obj = findFileTransferObject(socket);if (obj != nullptr){obj->sentSize += bytes;if (obj->sentSize == obj->fileSize){obj->isFinished = true;socket->close();}else{// 继续发送剩余的数据}}}
}void MainWindow::onSendError(QAbstractSocket::SocketError error)
{QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());if (socket != nullptr){FileTransferObject* obj = findFileTransferObject(socket);if (obj != nullptr){obj->isSending = false;obj->isAccepted = false;emit sendFileError(obj->fileName, obj->senderName, obj->receiverName, socket->errorString());// 关闭socket}}
}
在开始发送文件时,会创建一个QTcpSocket对象,然后连接到接收方的IP地址和端口号。当连接成功后,会发送一个包含文件名和文件大小的消息,让接收方做好接收文件的准备。接着,会按照一定的数据块大小,将文件分块发送到接收方。每发送一个数据块,都会记录已发送的文件大小,以便在下次发送时从上次发送的位置开始继续发送。
在发送文件的过程中,需要不断地检测已发送的文件大小是否等于文件总大小,以判断是否已经发送完毕。如果已经发送完毕,就会将isFinished设置为true,然后关闭TCP连接。如果出现发送错误,就会设置isSending为false,将isAccepted设置为false,并关闭TCP连接。
18.3 、接收文件
当MyselfQQ接收到文件传输请求时,会弹出一个文件传输接收窗口,用户可以选择接收或拒绝文件传输。
文件传输接收窗口的代码如下:
void MainWindow::showFileTransferDialog(FileTransferObject* obj)
{FileTransferDialog* dialog = new FileTransferDialog(obj, this);connect(dialog, SIGNAL(accepted(FileTransferObject*)), this, SLOT(onFileTransferAccepted(FileTransferObject*)));connect(dialog, SIGNAL(rejected(FileTransferObject*)), this, SLOT(onFileTransferRejected(FileTransferObject*)));dialog->show();
}
如果用户选择接收文件,就会开始接收文件。接收文件的过程采用TCP连接来完成,与发送文件的过程类似。
接收文件的关键代码如下:
void MainWindow::startReceiveFile(FileTransferObject* obj)
{QTcpSocket* socket = new QTcpSocket(this);obj->socket = socket;connect(socket, SIGNAL(connected()), this, SLOT(onReceiveConnected()));connect(socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onReceiveError(QAbstractSocket::SocketError)));socket->connectToHost(obj->senderIp, obj->senderPort);obj->isSending = false;obj->isAccepted = true;obj->isFinished = false;
}void MainWindow::onReceiveConnected()
{QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());if (socket != nullptr){FileTransferObject* obj = findFileTransferObject(socket);if (obj != nullptr){QByteArray block;QDataStream in(&block, QIODevice::ReadOnly);in.setVersion(QDataStream::Qt_5_9);qint64 blockSize = qint64(0);qint64 fileSize = qint64(0);QString fileName;socket->bytesAvailable();while (socket->bytesAvailable() < sizeof(qint64) * 2 + sizeof(QString)){if (!socket->waitForReadyRead(30000)){socket->close();return;}}in >> blockSize;in >> fileSize;in >> fileName;obj->isAccepted = true;obj->fileSize = fileSize;obj->fileName = fileName;obj->sentSize = 0;}}
}void MainWindow::onReadyRead()
{QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());if (socket != nullptr){FileTransferObject* obj = findFileTransferObject(socket);if (obj != nullptr){if (obj->isFinished){return;}if (obj->sentSize == 0){obj->file = new QFile(obj->filePath);if (!obj->file->open(QIODevice::WriteOnly)){socket->close();return;}}qint64 bytesCount = qint64(0);QByteArray buffer;while (socket->bytesAvailable() > 0){buffer = socket->read(qMin(socket->bytesAvailable(), qint64(1024)));bytesCount += obj->file->write(buffer);obj->sentSize += bytesCount;}if (obj->sentSize == obj->fileSize){obj->isFinished = true;obj->file->close();socket->close();emit receiveFileFinished(obj->fileName, obj->senderName, obj->receiverName);}}}
}void MainWindow::onReceiveError(QAbstractSocket::SocketError error)
{QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());if (socket != nullptr){FileTransferObject* obj = findFileTransferObject(socket);if (obj != nullptr){obj->isSending = false;obj->isAccepted = false;emit receiveFileError(obj->fileName, obj->senderName, obj->receiverName, socket->errorString());// 关闭socket}}
}
当接收方与发送方建立TCP连接后,接收方会等待发送方发送一个消息,其中包含了文件名和文件大小。接收方会先读取这个消息,然后创建一个与文件名相同的文件,并开始接收发送方发送的数据。每接收一段数据,就会将其写入文件,并记录已接收的文件大小。当已接收的文件大小等于文件总大小时,就认为文件已经接收完成。
18.4 、保证传输的安全和稳定
在MyselfQQ中,为了保证文件传输的安全和稳定,采取了以下几个措施:
-
采用TCP连接传输文件,以保证传输的稳定性和安全性。
-
对文件进行分块传输,每个数据块的大小为1024字节,以减小传输过程中出现中断的风险。
-
在发送文件时,会记录已发送的文件大小,以便在下次发送时从上次发送的位置开始继续发送。
-
在接收文件时,会记录已接收的文件大小,以便在下次接收时从上次接收的位置开始继续接收。
-
在发送文件和接收文件时,会对TCP连接的错误进行处理,以保证传输的可靠性和稳定性。
18.5 、总结
MyselfQQ的文件传输功能采用TCP连接来实现,支持多种类型的文件传输,如文本、图片、语音和文件等。在文件传输过程中,采取了多种措施来保证传输的安全性和稳定性。开发者可以根据本文提供的代码示例学习和实践,以在自己的应用中实现类似的文件传输功能。
本章相关例程源码下载
1.Qt5开发及实例_CH1801.rar 下载
Qt5开发及实例_CH1801.rar
相关文章:

Qt5开发及实例V2.0-第十八章-Qt-MyselfQQ实例
Qt5开发及实例V2.0-第十八章-Qt-MyselfQQ实例 第18章-Qt MyselfQQ18.1 概述18.2 、发送文件18.3 、接收文件18.4 、保证传输的安全和稳定18.5 、总结 本章相关例程源码下载1.Qt5开发及实例_CH1801.rar 下载 第18章-Qt MyselfQQ 18.1 概述 MyselfQQ是一个基于Qt5框架开发的轻量…...

当下IT测试技术员的求职困境
从去年被裁到现在,自由职业的我已经有一年没有按部就班打卡上班了。期间也面试了一些岗位,有首轮就挂的,也有顺利到谈薪阶段最后拿了offer的,不过最后选择了拒绝。 基于自己近一年的面试求职经历,我想聊聊当下大家在求…...
MR混合现实情景实训教学
MR混合现实技术是一种将虚拟现实与现实场景相融合的创新技术,可以广泛应用于各个领域。其中,混合现实情景实训教学是MR技术的一个重要应用场景。 在医学专业方面,医学生常常需要通过实际操作来提升自己的技能水平,然而传统的实训方…...
嵌入式C++总结
1、new delete与malloc free区别 new delete是运算符,malloc free是函数。 前者不需要传入大小,后者需要。 前者会调用构造、析构函数,后者不会。 前者不需要强制转换,后者需要。 2、智能指针 智能指针是避免忘记释放动态申请对象…...

C语言之内存函数篇(3)
目录 memcpy memcpy的使用 memcpy的模拟实现 NO1. NO2. memcpy可否实现重叠空间的拷贝 my_memcpy memcpy memmove memmove memmove 分析 代码 memset memset的使用 memcmp memcmp的使用 <0 0 >0 今天我们继续介绍几个重要的内存操作函数。&…...

java面试题-学成在线项目
1、详细说说你的项目吧 从以下几个方面进行项目介绍: 1、项目的背景,包括:是自研还是外包、什么业务、服务的客户群是谁、谁去运营等问题。 2、项目的业务流程 3、项目的功能模块 4、项目的技术架构 5、个人工作职责 6、个人负责模块的详细说…...
ViewBinding——Android之视图绑定
高版本的gradle不再支持 kotlin-android-extensions插件,因此view的绑定方式也有所改变。 1.启用视图绑定 android {...viewBinding {enabled true}} 如果想在生成绑定类时忽略某个布局文件,请将 tools:viewBindingIgnore"true" 属性添加到…...
vue学习-04vue的props配置项和mixin混入
今天仍然就是敲vue的一个demo,vue的props配置项和mixin混入 props配置项 Vue.js 中的 props 是用于在父组件向子组件传递数据的配置项。通过 props,你可以将父组件中的数据传递给子组件,并在子组件中使用这些数据。以下是关于 props 配置项…...

九、多项式朴素贝叶斯算法(Multinomial NB,Multinomial Naive Bayes)(有监督学习)
Multinomial Naive Bayes:用于多项式模型的Naive Bayes分类器 一、算法思路 多项式Naive Bayes分类器适用于离散特征分类(如文本分类中的字数) 多叉分布通常需要整数特征计数 不过,在实际应用中,分数计数(…...

数据结构上机练习——单链表的基本操作、头文件、类定义、main函数、多种链表算法的实现,含注释
文章目录 单链表的基本操作实现1.头文件2.类定义和多种算法的实现2.1创建空表2.2头插法创建n个元素的线性链表2.3一个带头节点的链表存放一组整数,设计一个算法删除值等于x的所有节点。2.4计算线性表中值为偶数的节点个数2.5一个带头节点的单链表heada存放一组整数&…...

如何通过AI视频智能分析技术,构建着装规范检测/工装穿戴检测系统?
众所周知,规范着装在很多场景中起着重要的作用。违规着装极易增加安全隐患,并且引发安全事故和质量问题,例如,在化工工厂中,倘若员工没有穿戴符合要求的特殊防护服和安全鞋,将有极大可能受到有害物质的侵害…...

C语言自定义类型(上)
大家好,我们又见面了,这一次我们来学习一些C语言有关于自定义类型的结构。 目录 1.结构体 2位段 1.结构体 前面我们已经学习了一些有关于结构体的知识,现在我们进行深入的学习有关于它的知识。 结构是一些值的集合,这些值称为…...
Python - 小玩意 - 圣诞树背景音乐弹窗
import turtle as t import tkinter as tk import pygame import random as r import threading import time# 初始化背景音乐 def initialize_music():file r"./music/周杰伦-蜗牛.mp3"pygame.mixer.init()pygame.mixer.music.load(file)pygame.mixer.music.play()…...

The 2023 ICPC Asia Regionals Online Contest (1) E. Magical Pair(数论 欧拉函数)
题目 T(T<10)组样例,每次给出一个n(2<n<1e18), 询问多少对,满足 答案对998244353取模,保证n-1不是998244353倍数 思路来源 OEIS、SSerxhs、官方题解 2023 ICPC 网络赛 第一场简要题解 - 知乎 题解 官方题解还没有…...

<十三>objectARX开发:模拟实现CAD的移动Move命令
一、目的 实现类似于CAD的移动命令,选择对象,移动到指定位置,移动过程中对象跟随鼠标移动。效果如下: 二、关键步骤 选择对象,打开实体判断类型:acedEntSel()、acdbOpenObject()、isKindOf()。指定基点:acedGetPoint()。移动模型,追踪光标移动对象实体:acedGrRead()…...
Autosar基础:模式管理-EcuM
ECUM目录 前言一、ECUM状态机二、Fixed和Flexible模式的区别与联系三、状态详解3.1.Startup3.2.UP3.3.RUN3.4.Sleep3.5.Shutdown三、EcuM唤醒源3.1 CAN Trcv唤醒3.2 唤醒后操作前言 根据Autosar对于模式管理的需求定义,模式管理有以下模块: ①ECU State Manager(EcuM):管理…...
代码随想录Day42 | 01背包问题| 416. 分割等和子集
01背包问题(Acwing) 有 N 件物品和一个容量是 V的背包。每件物品只能使用一次。 第 i 件物品的体积是 vi,价值是 wi。 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。 输入…...

UML六大关系总结
UML六大关系有:继承、关系、聚合、组合、实现、依赖。分为通过图和代码总结这些关系。 1、继承 继承(Inheritance):表示类之间的继承关系,子类继承父类的属性和方法,并可以添加自己的扩展。 继承&#x…...

ElementUI基本介绍及登录注册案例演示
目录 前言 一.简介 二.优缺点 三.Element完成登录注册 1. 环境配置及前端演示 1.1 安装Element-UI模块 1.2 安装axios和qs(发送get请求和post请求) 1.3 导入依赖 2 页面布局 2.1组件与界面 3.方法实现功能数据交互 3.1 通过方法进行页面跳转 3.2 axios发送get请求 …...

Python爬虫-某网酒店评论数据
前言 本文是该专栏的第6篇,后面会持续分享python爬虫案例干货,记得关注。 本文以某网的酒店数据为例,采集对应酒店的评论数据。具体思路和方法跟着笔者直接往下看正文详细内容。(附带完整代码) 注意:本文的案例“数据集”,选用的是本专栏上一篇“Python爬虫-某网酒店数…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...

免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

stm32wle5 lpuart DMA数据不接收
配置波特率9600时,需要使用外部低速晶振...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合
作者:来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布,Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明,Elastic 作为 …...
微服务通信安全:深入解析mTLS的原理与实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言:微服务时代的通信安全挑战 随着云原生和微服务架构的普及,服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...