基于UDP的可靠传输,文件+目录(C++,Qt)

一、基础知识

UDP(UserDatagramProtocol)是一个简单的面向消息的传输层协议,尽管UDP提供标头和有效负载的完整性验证(通过校验和),但它不保证向上层协议提供消息传递,并且UDP层在发送后不会保留UDP 消息的状态。因此,UDP有时被称为不可靠的数据报协议。如果需要传输可靠性,则必须在用户应用程序中实现。

所以为了利用UDP的传输速度,并保证传输的可靠性,需要在UDP的基础上实现可靠的数据传输协议。为了开发的效率,作者选择了开源的UDT做为基础,并在其基础上构建文件和目录的传输。

二、服务端

数据结构/配置定义

pub.h 定义通用数据类型和转换方法

#pragma once#include <QObject>using namespace std;///请求类型定义
enum reqType
{finished = -1, ///finished the sending.file, folder
};///请求定义
struct fileReq
{int type; //reqTypeQString name; //name for file or folderfileReq(){type = reqType::file;}
};///QString转string
string _Q2S(QString &qstr);
///string转QString
QString _S2Q(string &str);

config.h 定义基础配置信息

#pragma once#include <QObject>class config : public QObject
{Q_OBJECTpublic:config(QObject *parent = nullptr);~config();///获取本地存储路径QString getStoreDir() { return m_StoreDir; };///获取本地服务端口QString getPort() { return m_Port; };///设置本地存储路径void setStoreDir(QString dir) { m_StoreDir = dir; };///设置本地服务端口void setPort(QString port) { m_Port = port; };
private:///本地存储路径QString m_StoreDir;///本地服务端口QString m_Port;
};///获取全局配置对象
config *getConfig();

监听和接收连接

建立主线程,接收客户端的连接,并对每个连接开启新的子线程。

void mainThread::run()
{emit log("listening...");sockaddr_storage clientaddr;int addrlen = sizeof(clientaddr);UDTSOCKET fhandle;while (m_start){if (UDT::INVALID_SOCK == (fhandle = UDT::accept(m_serv, (sockaddr *)&clientaddr, &addrlen))){emit log(UDT::getlasterror().getErrorMessage());break;}char clienthost[NI_MAXHOST];char clientservice[NI_MAXSERV];getnameinfo((sockaddr *)&clientaddr, addrlen, clienthost, sizeof(clienthost), clientservice, sizeof(clientservice), NI_NUMERICHOST | NI_NUMERICSERV);QString msg = QString("new connection: %1:%2").arg(clienthost).arg(clientservice);emit log(msg);startClientThread(fhandle);}emit log("Service end.");
}

处理请求

子线程执行主要为三部分:1. 接收和解析请求 2. 处理请求 3. 传输结束

void fileThread::run()
{getReqs(m_fileReq);foreach(fileReq req, m_fileReq)procReq(req);sendEnd();
}

接收请求方法中,将会从客户端接收请求头的大小,然后接收请求的具体内容,并填充至请求列表。请求支持单个文件或目录类型。

void fileThread::getReqs(QList<fileReq> &reqlist)
{reqlist.clear();// aquiring file name information from clientchar file[1024];int len;if (UDT::ERROR == UDT::recv(m_client, (char*)&len, sizeof(int), 0)){QString msg ="getReqs size: " + QString::fromLocal8Bit(UDT::getlasterror().getErrorMessage());emit log(msg);return;}if (UDT::ERROR == UDT::recv(m_client, file, len, 0)){QString msg = "getReqs: " + QString::fromLocal8Bit(UDT::getlasterror().getErrorMessage());emit log(msg);return;}file[len] = '\0';QString info = QString::fromUtf8(file);QStringList reqs = info.split(";");foreach(QString req, reqs){QStringList file = req.split(",");if (file.size() > 1){fileReq r;r.type = file.at(0).toInt();r.name = file.at(1);reqlist.append(r);}}
}

获取请求列表后,按照文件和目录类型以此处理。

void fileThread::procReq(fileReq &req)
{if (req.type == reqType::file)sendFile(req);else if (req.type == reqType::folder)sendDir(req);
}

针对目录类型,需要进行递归遍历,获取文件并发送。

每次发送文件之前,需要先发送头信息,以告知客户端发送类型、文件名称和文件大小。

bool fileThread::sendHeader(fileReq &req, int64_t fileSize)
{QString header = QString("%1,%2,%3").arg(req.type).arg(req.name).arg(fileSize);QByteArray arr = header.toUtf8();int size = arr.size();// send header size informationif (UDT::ERROR == UDT::send(m_client, (char*)&size, sizeof(int), 0)){errUDT( "send header size");return false;}// send header informationif (UDT::ERROR == UDT::send(m_client, arr.data(), size, 0)){errUDT("send header");return false;}return true;
}

执行文件发送:

bool fileThread::sendFile(fileReq &req, const QString &root, bool keepDir)
{QString fullname = joinFullPath(getConfig()->getStoreDir(), root, req.name);fullname = fullname.replace("/", "\\");string file = _Q2S(fullname);//open the filefstream ifs(file, ios::in | ios::binary);ifs.seekg(0, ios::end);int64_t size = ifs.tellg();ifs.seekg(0, ios::beg);// send the headerif (!keepDir){QFileInfo info(req.name);req.name = info.fileName();}	if(!sendHeader(req, size)) return false;UDT::TRACEINFO trace;UDT::perfmon(m_client, &trace);// send the fileint64_t offset = 0;if (UDT::ERROR == UDT::sendfile(m_client, ifs, offset, size)){errUDT("sendfile");return false;}UDT::perfmon(m_client, &trace);cout << "speed = " << trace.mbpsSendRate << "Mbits/sec" << endl;QString msg = QString("speed = %1 Mb/s").arg(trace.mbpsSendRate);emit log(msg);ifs.close();return true;
}

三、客户端

config.h配置定义

#pragma once#include <QObject>class config : public QObject
{Q_OBJECTpublic:config(QObject *parent = nullptr);~config();///获取主机名/IPQString getHost() { return m_Host; };///获取端口QString getPort() { return m_Port; };///获取本地保存目录QString getLocalDir() { return m_LocalDir; };///获取服务端待下载文件名QString getServerFile() { return m_ServerFile; };///获取服务端待下载目录QString getServerDir() { return m_ServerDir; };///设置主机名/IPvoid setHost(QString host) { m_Host = host; };///设置端口void setPort(QString port) { m_Port = port; };///设置本地保存目录void setLocalDir(QString dir) { m_LocalDir = dir; };///设置服务端待下载文件名void setServerFile(QString file) { m_ServerFile = file; };///设置服务端待下载目录void setServerDir(QString dir) { m_ServerDir = dir; };
private:///主机名/IPQString m_Host;///端口QString m_Port;///本地保存目录QString m_LocalDir;///服务端待下载文件名QString m_ServerFile;///服务端待下载目录QString m_ServerDir;
};config *getConfig();

客户端负责实现请求的发送和文件接收。

发送请求

bool fileThread::sendReq()
{// send name information of the requested fileQStringList reqs;if (!getConfig()->getServerFile().isEmpty())reqs.append(QString("%1,%2").arg(reqType::file).arg(getConfig()->getServerFile()));if (!getConfig()->getServerDir().isEmpty())reqs.append(QString("%1,%2").arg(reqType::folder).arg(getConfig()->getServerDir()));QByteArray reqFile = reqs.join(";").toUtf8();int len = reqFile.size();if (UDT::ERROR == UDT::send(m_client, (char*)&len, sizeof(int), 0)){cout << "send: " << UDT::getlasterror().getErrorMessage() << endl;return false;}if (UDT::ERROR == UDT::send(m_client, reqFile.data(), len, 0)){cout << "send: " << UDT::getlasterror().getErrorMessage() << endl;return false;}return true;
}

接收文件

先接收头信息,然后接收文件数据。

bool fileThread::recvFile()
{// get size informationbool result = false;int len;if (UDT::ERROR == UDT::recv(m_client, (char*)&len, sizeof(int), 0)){errUDT("recv header size");return result;}char buffer[1024];if (UDT::ERROR == UDT::recv(m_client, buffer, len, 0)){errUDT("recv header");return result;}buffer[len] = '\0';QString info = QString::fromUtf8(buffer);QStringList ls = info.split(",");if (ls.size() < 3){emit log("recvFile: illegal params.");return result;}int type = ls.at(0).toInt();QString filename = ls.at(1);if (type == reqType::finished){emit log("server side finished.");return false;}else if (type == reqType::folder) ///dir{QString path = getConfig()->getLocalDir() + "\\" + filename;QDir dir;bool result = dir.mkpath(path);if (!result)emit log("failed to make dir: "+ path);return result;}int64_t size = ls.at(2).toInt();if (size < 0){emit log("no such file: " + filename);return false;}UDT::TRACEINFO trace;UDT::perfmon(m_client, &trace);// receive the fileQString path = getConfig()->getLocalDir() + "\\" + filename;string localFile = _Q2S(path.replace("/","\\"));fstream ofs(localFile, ios::out | ios::binary | ios::trunc);int64_t recvsize;int64_t offset = 0;if (UDT::ERROR == (recvsize = UDT::recvfile(m_client, ofs, offset, size))){errUDT("recvfile");}elseresult = true;UDT::perfmon(m_client, &trace);emit log(QString("speed = %1 Mb/s").arg(trace.mbpsRecvRate));ofs.close();return result;
}

四、程序示例

服务端存储目录

启动服务端

启动客户端

执行下载“测试”目录后的结果如下图:

接收目录:

 

 可执行程序链接:

(27条消息) 【免费】【可执行程序】基于UDT的文件+目录可靠传输(C++,Qt)资源-CSDN文库

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

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

相关文章

K8S初级入门系列之一-概述

一、前言 K8S经过多年的发展&#xff0c;构建了云原生的基石&#xff0c;成为了云原生时代的统治者。我将用三个博客系列全面&#xff0c;循序渐进的介绍K8S相关知识。 初级入门系列&#xff0c;主要针对K8S初学者&#xff0c;以及希望对K8S有所了解的研发人员&#xff0c;重点…

机器学习术语解析与应用(二)

文章目录 &#x1f340;目标函数&#xff08;Objective Function&#xff09;&#x1f340;GPU加速&#xff08;GPU Acceleration&#xff09;&#x1f340;迁移学习&#xff08;Transfer Learning&#xff09;&#x1f340;自然语言处理&#xff08;Natural Language Processi…

1 快速构建mybatis项目

1.1 使用Maven的quickstart框架 注意是不出现w的quickstart&#xff1a; 1.2 加入依赖 <dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</s…

设计模式之策略模式

定义一系列的算法&#xff0c;把他们一个个封装起来&#xff0c;并且使他们可以相互替代。本模式使得算法可独立于使用它的客户而变化! 痛点 策略模式可以很好解决众多if问题 如以下&#xff1a; package com.tao.YanMoDesignPattern.Strategy.notPattern;/*** Author Mi_Ta…

安装 PyCharm

网址&#xff1a;Download PyCharm: Python IDE for Professional Developers by JetBrains 安装文件&#xff1a; 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a; 正在安装&#xff1a; 安装完成&#xff1a;

【高阶数据结构】B树

文章目录 一、B-树1. 常见的搜索结构2. B树概念3. B-树的查找4. B-树的插入分析 二、B树和B*树1. B树2. B*树 三、B-树的应用1. 索引2. MySQL索引简介2.1 MyISAM2.2 InnoDB 一、B-树 1. 常见的搜索结构 种类数据格式时间复杂度顺序查找无要求O(N)二分查找有序O(log2N)二叉搜索…

RocksDB架构

1、rocksdb是什么? RocksDB中文网 | 一个持久型的key-value存储 rocksdb是一种KV存储引擎&#xff0c;常用于数据库存储数据&#xff0c;无法直接使用&#xff0c;没有提供sql命令&#xff0c;通过调用rocksdb提供的api进行数据库的读写等操作。 rocksdb是以leveldb为基础开…

聊聊spring-cloud的负载均衡

聊聊spring-cloud的负载均衡 1. 选择合适的负载均衡算法2. 合理设置超时时间3. 缓存服务实例列表4. 使用断路器5. 使用缓存Spring Cloud负载均衡组件对比RibbonLoadBalancerWebClient对比 总结 在微服务架构中&#xff0c;负载均衡是非常重要的一个环节&#xff0c;可以有效地提…

S32K144 GPIO外设分析

1. S32K144 GPIO外设特性 下面的内容来自于S32K用户手册的翻译&#xff0c;或者网上关于S32K系列的一些pdf文件介绍。有些内容可能会出现理解不到位或者翻译错误方面&#xff0c;如果大家有疑问最好可以查阅用户手册。 GPIO和PORT的数量 从用户手册&#xff0c;对于PCR&#x…

React Dva项目中路由跳转的方法

接下来 我们来看看路由跳转 先打开 我们Dva项目 然后我们需要在routes 下创建一个自己的路由&#xff0c;如果您尚未掌握在Dva项目中创建路由&#xff0c;可以参考我的文章 React 在Dva项目中修改路由配置&#xff0c;并创建一个自己的路由 然后 我的项目有两个路由 router.js…

ASFF Learning Spatial Fusion for Single-Shot Object Detection 论文学习

1. 解决了什么问题&#xff1f; 目标检测取得了显著成绩&#xff0c;但是检测不同尺度的目标仍然是一个挑战。金字塔或多层级特征是解决目标检测中尺度变化的常用手段。但对于单阶段目标检测器而言&#xff0c;各特征尺度之间不一致性制约了算法的表现。与图像金字塔相比&…

VMware Workstation 18 Tech Preview - 增强的 Windows 11 虚拟机安全性

VMware Workstation 18 Tech Preview - 增强的 Windows 11 虚拟机安全性 VMware Workstation Tech Preview 2023 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-workstation-18/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xf…

Pytorch个人学习记录总结 07

目录 神经网络-非线性激活 神经网络-线形层及其他层介绍 神经网络-非线性激活 官方文档地址&#xff1a;torch.nn — PyTorch 2.0 documentation 常用的&#xff1a;Sigmoid、ReLU、LeakyReLU等。 作用&#xff1a;为模型引入非线性特征&#xff0c;这样才能在训练过程中…

[k8s] command和args

k8s中的command和args可以覆盖docker镜像中的entrypoint和cmd。其中&#xff0c;k8s-command可以覆盖docker-entrypoint&#xff0c;k8s-args可以覆盖docker-cmd。参考Difference between Docker ENTRYPOINT and Kubernetes container spec COMMAND? 了解一下entrypoint的意义…

Spring 更简单的读取和存储对象

目录 1.存储 Bean 对象 1.1 前置⼯作&#xff1a;配置扫描路径 1.2 添加注解存储 Bean 对象 1.2.1 Controller&#xff08;控制器存储&#xff09; 1.2.2 Service&#xff08;服务存储&#xff09; 1.2.3 Repository&#xff08;仓库存储&#xff09; 1.2.4 Component&a…

C++---string

String C语言中的字符串和C中的string类标准库中的string类string类的常用接口string类对象的常见构造string类对象的容量操作string类对象的访问及遍历操作 C语言中的字符串和C中的string类 在C语言中&#xff0c;字符串是一个字符数组&#xff0c;它以空字符\0结尾&#xff…

【数据结构】朴素模式匹配 KMP算法

&#x1f387;【数据结构】朴素模式匹配 & KMP 算法&#x1f387; &#x1f308; 自在飞花轻似梦,无边丝雨细如愁 &#x1f308; &#x1f31f; 正式开始学习数据结构啦~此专栏作为学习过程中的记录&#x1f31f; 文章目录 &#x1f387;【数据结构】朴素模式匹配 & K…

【数据架构】Data Fabric 架构是实现数据管理和集成现代化的关键

D&A 领导者应该了解数据编织架构的关键支柱&#xff0c;以实现机器支持的数据集成。 在日益多样化、分布式和复杂的环境中&#xff0c;数据管理敏捷性已成为组织的任务关键优先事项。为了减少人为错误和总体成本&#xff0c;数据和分析 (D&A) 领导者需要超越传统的数据…

MyBatis操作数据库

1.MyBatis是什么&#xff1f; MyBatis 是⼀款优秀的持久层框架&#xff0c;它⽀持⾃定义 SQL、存储过程以及⾼级映射。MyBatis 去除了⼏乎所有的 JDBC 代码以及设置参数和获取结果集的⼯作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接⼝和 Java POJO&#xf…

【机器学习】吃瓜教程 | 西瓜书 + 南瓜书 (1)

文章目录 一、绪论1、什么是机器学习&#xff1f;2、基本术语3、假设空间4、归纳偏好5、发展历程 二、模型评估与选择A、一种训练集一种算法2.1 经验误差 与 过拟合2.2 评估方法a) 留出法b) 交叉验证法c) 自助法d) 调参与最终模型 2.3 性能度量a) 错误率与精度b) 查准率、查全率…