Linux(CentOS)/Windows-C++ 云备份项目(服务器网络通信模块,业务处理模块设计,断点续传设计)
此模块将网络通信模块和业务处理模块进行了合并
- 网络通信通过httplib库搭建完成
- 业务处理:
- 文件上传请求:备份客户端上传的文件,响应上传成功
- 客户端列表请求:客户端请求备份文件的请求页面,服务器响应
- 文件下载请求:通过展示的文件列表,点击下载,服务器响应下载的文件数据
文章目录
- 1. 网络通信模块设计
- 2. 业务处理模块设计
- 文件上传业务处理 /upload请求
- 展示备份文件页面 / /listshow请求
- 文件下载业务处理 /download
- 断点续传原理:
- 3. 服务器代码:
- 4. 代码位置
1. 网络通信模块设计
文件下载Http请求部分如下:通过分隔符可以取到文件数据和文件的其他信息,文件信息和文件内容之间空行隔开。这个解析过程httplib库已经封装完毕

网络通信请求设计:
- 文件上传:服务器收到/upload 是为文件上传
- 展示页面:服务器收到/listshow是服务器所有备份文件展示
响应 HTTP/1.1 200 OK + 构造html正文界面 - 文件下载:服务器收到/download/文件名 为文件下载请求
响应 HTTP/1.1 200 OK +文件数据(正文)
2. 业务处理模块设计
文件上传业务处理 /upload请求
#pragma once
#include "backups.hpp"
#include "./httplib/httplib.h"
#include "./config/config.hpp"
extern CloudBackups::DataMange *dataMange;
namespace CloudBackups
{class Server{private:int port;std::string ip;std::string download_prefix;httplib::Server server;// 上传文件static void Upload(const httplib::Request &request, httplib::Response &response){LOG(INFO, "upload begin");// POST请求,文件数据在http正文中,分区存储bool ret = request.has_file("file"); // 判断有无上传文件字段if (ret == false){LOG(ERROR, "request error!");response.status = 400;return;}// 获取数据const auto &file = request.get_file_value("file");std::string backdir = Config::GetInstance()->GetBackDir();// 保存文件std::string filepath = backdir + FileUtil(file.filename).filename(); // 实际路径+文件名FileUtil stream(filepath);stream.setContent(file.content);// 更新文件信息Json文件BackupInfo info(filepath);dataMange->Insert(info);LOG(INFO, "upload success");}// 展示页面static void ListShow(const httplib::Request &request, httplib::Response &response){}// 下载文件static void Download(const httplib::Request &request, httplib::Response &response){}public:Server(){Config *config = Config::GetInstance();port = config->GetServerPort();ip = config->GetServerIp();download_prefix = config->GetDownloadPrefix();LOG(INFO, "init server success");}bool RunMoudle(){LOG(INFO, "server running");// 搭建Http服务器server.Post("/upload", Upload); // 文件上传server.Get("/list", ListShow); // 展示页面server.Get("/", ListShow); // 网页根目录也是展示页面std::string download_url = download_prefix + "(.*)";server.Get(download_url, Download); // 下载文件,正则表达式捕捉要下载的文件if (server.listen(ip, port) == false){LOG(FATAL, "server listen failed! ip=" + ip);return false;}return true;}};
}
单元测试运行截图
// #include "util/fileutil.hpp"
#include <vector>
#include "util/json.hpp"
#include "config/config.hpp"
#include "backups.hpp"
#include "hot.hpp"
#include "server.hpp"
CloudBackups::DataMange *dataMange;
void ServerUtilTest()
{CloudBackups::Server server;dataMange = new CloudBackups::DataMange();server.RunMoudle();
}
int main(int argc, char const *argv[])
{ServerUtilTest();return 0;
}

上传文件的信息Json如下:

展示备份文件页面 / /listshow请求
#pragma once
#include "backups.hpp"
#include "./httplib/httplib.h"
#include "./config/config.hpp"
extern CloudBackups::DataMange *dataMange;
namespace CloudBackups
{class Server{private:int port;std::string ip;std::string download_prefix;httplib::Server server;// 上传文件static void Upload(const httplib::Request &request, httplib::Response &response){}// 展示页面static void ListShow(const httplib::Request &request, httplib::Response &response){LOG(INFO, "list show begin");// 获取所有文件信息std::vector<BackupInfo> array;dataMange->GetAll(array);// 根据所有文件信息构建http响应std::stringstream ss;ss << R"(<!DOCTYPE html><html lang="cn"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>download list</title></head><body>)";ss << R"(<h1 align="center">Download List</h1>)";for (auto &info : array){std::string filename = FileUtil(info.real_path).filename();ss << R"(<tr><td><a href=")" << info.url << R"(">)" << filename << "</a></td>";ss << R"(<td align="right"> )" << convertTimeStamp2TimeStr(info.mtime) << " </td>";ss << R"(<td align="right">)" << info.size / 1024 << "Kb</td></tr>";ss << "<br>";}ss << "</body></html>";response.body = ss.str();response.set_header("Content-Type", "text/html");response.status = 200;LOG(INFO, "list show end");}// 下载文件static void Download(const httplib::Request &request, httplib::Response &response){}public:Server(){Config *config = Config::GetInstance();port = config->GetServerPort();ip = config->GetServerIp();download_prefix = config->GetDownloadPrefix();LOG(INFO, "init server success");}bool RunMoudle(){LOG(INFO, "server running");// 搭建Http服务器server.Post("/upload", Upload); // 文件上传server.Get("/list", ListShow); // 展示页面server.Get("/", ListShow); // 网页根目录也是展示页面std::string download_url = download_prefix + "(.*)";server.Get(download_url, Download); // 下载文件,正则表达式捕捉要下载的文件if (server.listen(ip, port) == false){LOG(FATAL, "server listen failed! ip=" + ip);return false;}return true;}};
}


文件下载业务处理 /download
http的ETag头部字段:存储了一个资源的唯一标识
客户端第一次请求文件时会收到响应信息。
客户端第二次下载时,客户端会把这个信息发送给服务器,让这个服务器根据这个标识判断这个资源有没有被修改锅。如果没修改过。客户端直接使用缓存区的资源。如果改过则重新修改
http对ETag字段没有定义,这里设定:
ETags:文件名称-文件大小-最后修改时间 构成
ETags字段也用于断点续传,断点续传也需要保证文件没有被修改
http协议的Accept-Ranges:bytes字段用于表示支持断点续传。数据以字节结尾
Content-Type:字段决定了浏览器如何处理响应正文,用来区分下载还是html显示。
Content-Type:application/octet-stream常用于文件下载
断点续传原理:
文件下载时由于异常而中断,如果从头下载效率较低,需要将之前传输过的数据效率太低。断点续传的目的为了提高上传效率
实现:客户端在下载时需要记录当前下载的位置。当下载中断时,下次断点续传时将下载起始位置发送给服务器。服务器收到后仅仅回传客户端需要的数据即可
如果下载文件后这个文件在服务器上被修改了,这时候需要将文件重新下载
http中断点续传关键点在于告诉服务器下载区间范围,服务器上要检测这个文件是否被修改。
http协议的Accept-Ranges:bytes字段用于表示支持断点续传
ETag文件唯一标识符,客户端收到响应会保存这个信息
请求:
GET /download/test.txt HTTP/1.1
If-Range:“服务端在下载时响应ETag字段搭配使用判断文件是否被修改,常用于恢复下载”
Range: bytes=100-200(区间范围) 这个字段用来告诉客户端需要的数据范围
响应:
HTTP/1.1 206(服务器处理部分get请求) Paritial Content
ETag:”xxxx“(响应资源的版本标识符,判断文件是否被修改)
Content-Range: bytes 100-200(范围)
Accept-Ranges: bytes 字段用于表示支持断点续传正文就是对应区间的数据
真正实现时:cpp-httplib会自动根据请求Range字段对response.body进行切片返回,封装实现。直接把response.body全部设置为文件所有内容即可
#pragma once
#include "backups.hpp"
#include "../httplib/httplib.h"
#include "../config/config.hpp"
extern CloudBackups::DataMange *dataMange;
namespace CloudBackups
{class Server{private:int port;std::string ip;std::string download_prefix;httplib::Server server;// ETag为设计者自行指定 ETags:文件名称-文件大小-最后修改时间 构成static std::string GetETag(BackupInfo info){std::string etag = FileUtil(info.real_path).filename();etag += "-";etag += std::to_string(info.size);etag += "-";etag += std::to_string(info.mtime);return etag;}// 下载文件static void Download(const httplib::Request &request, httplib::Response &response){// 1. 获取客户端请求资源的路径 request.path// 2. 根据路径获取文件备份信息BackupInfo info;if (dataMange->GetByUrl(request.path, info) == false){LOG(WARNING, "file /download not found");response.status = 404;return;}// 3. 判断文件是否被压缩,被压缩的话需要先解压缩,删除压缩包,修改备份信息if (info.packflag == true){// 被压缩,解压到backdir目录浏览FileUtil tool(info.pack_path);tool.unzip(info.real_path);// 删除压缩包tool.removeFile();info.packflag = false;// 修改配置文件dataMange->UpDate(info);}// 4. 读取文件数据放入body中FileUtil tool(info.real_path);tool.getContent(response.body);// 判断断点续传bool retrans = false; // 标记断点续传std::string befetag;if (request.has_header("If-Range")){// 断点续传 服务端在下载时响应ETag字段搭配使用判断文件是否被修改befetag = request.get_header_value("If-Range");if (befetag == GetETag(info)){// 文件没修改过retrans = true;}}// 没有If-Range字段或者If-Range字段与ETag不匹配,重新下载if (retrans == false){// 正常下载// 5. 设置响应头部字段ETag Accept-Range字段response.set_header("ETag", GetETag(info));response.set_header("Accept-Ranges", "bytes");response.set_header("Content-Type", "application/octet-stream");response.status = 200;}else{// 断点续传,了解区间范围response.set_header("ETag", GetETag(info));response.set_header("Accept-Ranges", "bytes");response.status = 206; // cpp-httplib会自动根据请求Range字段对response.body进行切片返回,封装实现}LOG(INFO, "download success");}public:Server(){Config *config = Config::GetInstance();port = config->GetServerPort();ip = config->GetServerIp();download_prefix = config->GetDownloadPrefix();// 创建文件夹FileUtil tool;tool.mkdir(Config::GetInstance()->GetBackDir());tool.mkdir(Config::GetInstance()->GetPackfileDir());LOG(INFO, "init server success");}bool RunMoudle(){LOG(INFO, "server running");// 搭建Http服务器server.Post("/upload", Upload); // 文件上传server.Get("/list", ListShow); // 展示页面server.Get("/", ListShow); // 网页根目录也是展示页面std::string download_url = download_prefix + "(.*)";// LOG(INFO, "DEBUG:" + download_url);server.Get(download_url, Download); // 下载文件,正则表达式捕捉要下载的文件if (server.listen(ip, port) == false){LOG(FATAL, "server listen failed! ip=" + ip);return false;}return true;}};
}
3. 服务器代码:
#include <vector>
#include "../util/json.hpp"
#include "../config/config.hpp"
#include "backups.hpp"
#include "hot.hpp"
#include "server.hpp"
#include <thread>
CloudBackups::DataMange *dataMange;
void ServerRun()
{CloudBackups::Server server;dataMange = new CloudBackups::DataMange();server.RunMoudle();
}
void HotRun()
{dataMange = new CloudBackups::DataMange();CloudBackups::HotMange hot;hot.RunModule();
}
int main(int argc, char const *argv[])
{// 启动热点管理模块std::thread hot_thread(HotRun);std::thread server_thread(ServerRun);hot_thread.join();server_thread.join();return 0;
}
4. 代码位置
至此,项目服务器所有业务处理完毕
Gitee
Github
相关文章:
Linux(CentOS)/Windows-C++ 云备份项目(服务器网络通信模块,业务处理模块设计,断点续传设计)
此模块将网络通信模块和业务处理模块进行了合并 网络通信通过httplib库搭建完成业务处理: 文件上传请求:备份客户端上传的文件,响应上传成功客户端列表请求:客户端请求备份文件的请求页面,服务器响应文件下载请求&…...
【QQ版】QQ群短剧机器人源码 全网短剧机器人插件
内容目录 一、详细介绍二、效果展示2.效果图展示 三、学习资料下载 一、详细介绍 QQ版本可以兼容两个框架(HTQQ,MYQQ这两个的vip版也可以使用) 支持私聊与群聊,命令是 搜剧影视关键词 如果无法搜索到影视资源,请使用下方命令&…...
矩阵间关系的建立
参考文献 2-D Compressive Sensing-Based Visually Secure Multilevel Image Encryption Scheme 加密整体流程如下: 我们关注左上角这一部分: 如何在两个图像之间构建关系,当然是借助第3个矩阵。 A. Establish Relationships Between Different Images 简单说明如下: …...
【C++】C到C++的入门知识
目录 1、C关键字 2、命名空间 2.1 命名空间的定义 2.2 命名空间的使用 2.2.1 加命名空间名称及作用域限定符 2.2.2 使用using将命名空间中某个成员引入 2.2.3 使用using namespace 命名空间名称引入 3、C输入&输出 4、缺省参数 4.1 缺省参数的概念 4.2 缺省参数的…...
【c++】简单的日期计算器
🔥个人主页:Quitecoder 🔥专栏:c笔记仓 朋友们大家好啊,在我们学习了默认成员函数后,我们本节内容来完成知识的实践,来实现一个简易的日期计算器 目录 头文件声明函数函数的实现1.全缺省默认构…...
基于easyx库的C/C++游戏编程实例-飞机大战
飞机大战游戏设计 首先创建飞机/子弹结构: struct Plane {int x;int y;bool live;int width;int height;int type;int hp; }player,bul[BUL_NUM],enemy[ENE_NUM];你需要加载图片: void ImageLoad() {//背景loadimage(&bg[0], "./image/飞机大…...
stitcher类实现多图自动拼接
效果展示 第一组: 第二组: 第三组: 第四组: 运行代码 import os import sys import cv2 import numpy as npdef Stitch(imgs,savePath): stitcher cv2.Stitcher.create(cv2.Stitcher_PANORAMA)(result, pano) stitcher.st…...
Ubuntu下udp通信
一、知识准备阶段 socket是什么?套接字是什么? https://blog.csdn.net/m0_37925202/article/details/80286946 Socket程序从Windows移植到Linux下的一些注意事项 sockaddr和sockaddr_in详解 bzero和memset函数 函数原型:void bzero&…...
拌合楼管理软件开发(十三) 对接耀华XK3190-A9地磅(实战篇)
前言: 实战开整 目前而言对于整个拌合楼管理软件开发,因为公司对这个项目还处于讨论中,包括个人对其中的商业逻辑也存在一些质疑,都是在做一些技术上的储备.很早就写好了串口与地磅对接获取代码,也大概知道真个逻辑,这次刚好跟库区沟通,远程连接到磅房电脑,开始实操一下. 一、地…...
FastAPI+React全栈开发10 MongoDB聚合查询
Chapter02 Setting Up the Document Store with MongoDB 10 Aggregation framework FastAPIReact全栈开发10 MongoDB聚合查询 In the following pages, we will try to provide a brief introducton to the MongoDB aggregation framework, what it is, what benefits it of…...
python 报错问题汇总
error: [WinError 32] 另一个程序正在使用此文件,进程无法访问。: d:\\anaconda\\envs\\yolov5\\lib\\site-packages\\ISR-2.2.0-py3.7.egg 解决方法:重启pycharm python-contrib 无法安装 opencv-contrib-python 安装包网址:安装包下载链接…...
6.5物联网RK3399项目开发实录-驱动开发之LCD显示屏使用(wulianjishu666)
90款行业常用传感器单片机程序及资料【stm32,stc89c52,arduino适用】 链接:https://pan.baidu.com/s/1M3u8lcznKuXfN8NRoLYtTA?pwdc53f LCD使用 简介 AIO-3399J开发板外置了两个LCD屏接口,一个是EDP,一个是LVDS,接口对应板…...
「Android高级工程师」BAT大厂面试基础题集合-下-Github标星6-5K
C、 com.android.provider.contact D、 com.android.provider.contacts 11.下面关于ContentProvider描述错误的是()。 A、 ContentProvider可以暴露数据 B、 ContentProvider用于实现跨程序共享数据 C、 ContentProvider不是四大组件 D、 ContentP…...
【算法】基数排序
简介 基数排序(*Radix sort)是一种非比较排序算法(non-comparative sorting algorithm)。现代计算机的基数排序算法由 计数排序 算法的开发人哈罗德H西华德(Harold H. Seward)于1954年于麻省理工大学开发。…...
2核2G服务器优惠价格轻量61元一年,CVM价格313元15个月
腾讯云2核2G服务器多少钱一年?轻量服务器61元一年,CVM 2核2G S5服务器313.2元15个月,轻量2核2G3M带宽、40系统盘,云服务器CVM S5实例是2核2G、50G系统盘。腾讯云2核2G服务器优惠活动 txybk.com/go/txy 链接打开如下图:…...
不同Python版本和wxPython版本用pyinstaller打包文件大小对比
1、确定wxPython和Python版本的对应关系 在这里可以找到Python支持的所有wxPython版本:https://pypi.tuna.tsinghua.edu.cn/simple/wxpython/ 由于Python从3.6版本开始支持f字符串、从3.9版本开始不支持Windows7操作系统,所以我仅筛选3.6-3.8之间的版本…...
【C语言】结构体详解(一)
目录 1、什么是结构体? 2、结构体成分 3、结构体变量的定义与初始化 3.1、结构体变量的三种定义方式 3.2、结构体变量的初始化 4、结构体成员的访问(两种方式) 4.1、直接访问 4.2、间接访问 5、结构的特殊声明 5.1、不完全声明(匿…...
AI时代-普通人的AI绘画工具对比(Midjouney与Stable Diffusion)
AI时代-普通人的AI绘画工具对比(Midjouney与Stable Diffusion) 前言1、基础对比Stable Diffusion(SD)SD界面安装与使用SD Midjouney(MJ) 2、硬件与运行要求对比Stable Diffusion硬件要求内存硬盘显卡 Midjo…...
【蓝桥杯】矩阵快速幂
一.快速幂概述 1.引例 1)题目描述: 求A^B的最后三位数表示的整数,A^B表示:A的B次方。 2)思路: 一般的思路是:求出A的B次幂,再取结果的最后三位数。但是由于计算机能够表示的数字…...
C语言使用STM32开发板手搓高端家居洗衣机
目录 概要 成品效果 背景概述 1.开发环境 2.主要传感器。 技术细节 1. 用户如何知道选择了何种功能 2.启动后如何进行洗衣 3.如何将洗衣机状态上传至服务器并通过APP查看 4.洗衣过程、可燃气检测、OLED屏显示、服务器通信如何并发进行 小结 概要 本文章主要是讲解如…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...
