Qt5开发及实例V2.0-第十八章-Qt-MyselfQQ实例

news2024/11/19 17:29:57

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中,发送文件的逻辑可以分为以下几个步骤:

  1. 创建文件传输对象

在发送文件之前,需要先创建文件传输对象。文件传输对象包含了发送方和接收方的相关信息,以及要发送的文件的路径、大小等信息。

文件传输对象的定义如下:

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连接。

  1. 获取接收方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地址和端口号,并开始发送文件。

  1. 开始发送文件

在获取到接收方的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中,为了保证文件传输的安全和稳定,采取了以下几个措施:

  1. 采用TCP连接传输文件,以保证传输的稳定性和安全性。

  2. 对文件进行分块传输,每个数据块的大小为1024字节,以减小传输过程中出现中断的风险。

  3. 在发送文件时,会记录已发送的文件大小,以便在下次发送时从上次发送的位置开始继续发送。

  4. 在接收文件时,会记录已接收的文件大小,以便在下次接收时从上次接收的位置开始继续接收。

  5. 在发送文件和接收文件时,会对TCP连接的错误进行处理,以保证传输的可靠性和稳定性。

18.5 、总结

MyselfQQ的文件传输功能采用TCP连接来实现,支持多种类型的文件传输,如文本、图片、语音和文件等。在文件传输过程中,采取了多种措施来保证传输的安全性和稳定性。开发者可以根据本文提供的代码示例学习和实践,以在自己的应用中实现类似的文件传输功能。



本章相关例程源码下载

1.Qt5开发及实例_CH1801.rar 下载

Qt5开发及实例_CH1801.rar

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

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

相关文章

技术学习群-第三周内容共享

本周为技术群聊的第三周&#xff0c;看看本周&#xff0c;都聊了些啥。 探讨u2以及appium 什么是u2&#xff1f;也就是uiautomator2&#xff0c;主要探讨的是uiautomator2与appium哪个更好用。 为什么探讨&#xff1f;他们拿来干什么的&#xff1f; 都是可以拿来做app自动化的&…

基于springboot+vue的毕业生实习与就业管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

C语言之字符函数字符串函数篇(1)

目录 前言 求字符串长度 strlen strlen统计的是字符串\0之前的字符串长度 字符指针 strlen的返回值是无符号整型 strlen的三种模拟实现 计数器 函数递归 指针_指针 长度不受限制的字符串函数 strcpy strcpy会将源字符串中的 \0 拷贝到目标空间 strcpy参数目标空…

cocos2dx查看版本号的方法

打开文件&#xff1a;项目根目录\frameworks\cocos2d-x\docs\RELEASE_NOTES.md 知道引擎版本号的意义&#xff1a; 1.面试中经常被问到(面试官想知道你会不会查版本号&#xff0c;你会查也不一定会去看&#xff0c;如果你去看了说明你是一个有心人&#xff0c;或者想深入研究下…

Innodb 原理和日志

一、MySQL结构 客户端 server层 查询缓存&#xff08;5.7&#xff09; 连接器 分析器 优化器 执行器 引擎层 二、一条update操作mysql的流程 三、MySQL的日志 &#xff08;1&#xff09;redo log 保证MySQL 持久性的关键&#xff0c;如果MySQL宕机&#xff0c;buffer pool…

Jupyter 报错:can‘t convert np.ndarray of type numpy.object_.

can’t convert np.ndarray of type numpy.object_. The only supported types are: float64, float32, float16, complex64, complex128, int64, int32, int16, int8, uint8, and bool. 解决方案&#xff1a;使用.astype(“float64”)强制转化

软件测试人员必须知道的接口测试基础

一、首先&#xff0c;什么是接口呢&#xff1f; 接口一般来说有两种&#xff0c;一种是程序内部的接口&#xff0c;一种是系统对外的接口。系统对外的接口&#xff1a;比如你要从别的网站或服务器上获取资源或信息&#xff0c;别人肯定不会把数据库共享给你&#xff0c;他只能…

[项目管理-34]:项目经理在强矩阵与弱矩阵管理中不同的作用

目录 一、项目管理与模式 1.1 项目管理 1.2 项目管理模式 1.3 项目管理的矩阵结构 二、项目管理不同矩阵结构的比较 2.1 职能型、项目型、矩阵型 2.2 弱矩阵、强矩阵、平衡矩阵 2.3 不同矩阵对项目经理的能力要求 一、项目管理与模式 1.1 项目管理 项目管理是在特定的…

一个关于 i++ 和 ++i 的面试题打趴了所有人

前言 都说大城市现在不好找工作&#xff0c;可小城市却也不好招人。 我们公司招了挺久都没招到&#xff0c;主管感到有些心累。 我提了点建议&#xff0c;是不是面试问的太深了&#xff0c;在这种小城市&#xff0c;能干活就行。 他说自己问的面试题都很浅显&#xff0c;如果答…

Learn Prompt- Midjourney 图片生成:基本设置和预设

/settings指令为模型版本、样式值、质量值和升级器版本等常用选项提供切换按钮。 备注 添加到提示末尾的参数将覆盖/settings中的设置。 模型版本​ 1️⃣ MJ Version 12️⃣ MJ Version 23️⃣ MJ Version 34️⃣ MJ Version 45️⃣ MJ Version 5&#x1f308; Niji Mode&a…

虚拟机如何连接MobaXterm

MobaXterm是一款增强型远程连接工具&#xff0c;Xshell相似。可以与虚拟机连接&#xff0c;从而在Windows界面下对其进行相关操作&#xff0c;使用非常的便捷 1.下载vmware并且在上面安装虚拟机任意版本。这里我安装的是centos7&#xff08;2009&#xff09; 2.打开终端 输入命…

TIM定时器

简介 • TIM &#xff08; Timer &#xff09;定时器 • 定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断 • 16 位计数器、预分频器、自动重装寄存器的时基单元&#xff0c;在 72MHz 计数时钟下可以实现最大 59.65s 的定时 • 不仅具备基本的定时中…

R语言贝叶斯非参数模型:密度估计、非参数化随机效应META分析心肌梗死数据...

全文链接&#xff1a;http://tecdat.cn/?p23785 最近&#xff0c;我们使用贝叶斯非参数&#xff08;BNP&#xff09;混合模型进行马尔科夫链蒙特卡洛&#xff08;MCMC&#xff09;推断&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 概述 相关视频 在这篇文…

R语言逻辑回归、决策树、随机森林、神经网络预测患者心脏病数据混淆矩阵可视化...

全文链接:https://tecdat.cn/?p33760 众所周知&#xff0c;心脏疾病是目前全球最主要的死因。开发一个能够预测患者心脏疾病存在的计算系统将显著降低死亡率并大幅降低医疗保健成本。机器学习在全球许多领域中被广泛应用&#xff0c;尤其在医疗行业中越来越受欢迎。机器学习可…

若依前后端分离如何解决匿名注解启动报错?

SpringBoot2.6.0默认是ant_path_matcher解析方式,但是2.6.0之后默认是path_pattern_parser解析方式。 所以导致读取注解类方法需要对应的调整,当前若依项目默认版本是2.5.x,如果使用大于2.6.x,需要将info.getPatternsCondition().getPatterns()修改为info.getPathPatterns…

保研CS/软件工程/通信专业问题汇总(搜集和自己遇到的)

机器学习 1.TP、TN、FP、FN、F1 2.机器学习和深度学习的区别和联系 模型复杂性&#xff1a;深度学习是机器学习的一个子领域&#xff0c;其主要区别在于使用深层的神经网络模型。深度学习模型通常包含多个隐层&#xff0c;可以学习更加复杂的特征表示&#xff0c;因此在某些任…

使用python处理MNIST数据集

文章目录 一. MNIST数据集1.1 什么是MNIST数据集1.2MNIST数据集文件格式1.3使用python访问MNIST数据集文件内容 附录程序源码 一. MNIST数据集 1.1 什么是MNIST数据集 MNIST数据集是入门机器学习/识别模式的最经典数据集之一。最早于1998年Yan Lecun在论文:[Gradient-based l…

Qt5开发及实例V2.0-第十九章-Qt.QML编程基础

Qt5开发及实例V2.0-第十九章-Qt.QML编程基础 第19章 QML编程基础19.1 QML概述19.1.1 第一个QML程序19.1.2 QML文档构成19.1.3 QML基本语法 19.2 QML可视元素19.2.1 Rectangle&#xff08;矩形&#xff09;元素19.2.2 Image&#xff08;图像&#xff09;元素19.2.3 Text&#xf…

JavaScript中的代理对象(proxy)

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 创建代理对象⭐ 使用代理对象⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友…

Helm 的简单使用 wordpress install

概述 尝试使用Helm部署wordpress博客服务 Helm | Helm Helm命令 bash自动补全 Helm | Helm补全 - bash wordpress案例 install helm repo add bitnami https://charts.bitnami.com/bitnamihelm install wordpress bitnami/wordpress \ --namespacewordpress \ --create-…