【C++项目】云备份项目(补充)——第三方库的使用

第三方库的使用

jsoncpp库序列化和反序列化

json 是一种数据交换格式,采用完全独立于编程语言的文本格式来存储和表示数据。

比如:一名学生的信息

char name = "小明";
int age = 18;
float score[3] = {88.5, 99, 58};

json这种数据交换格式是将这多种数据对象组织成为一个字符串

[
	{
		"姓名" : "小明",
		"年龄" : 18,
		"成绩" : [88.5, 99, 58]
	},
	{
		"姓名" : "小亮",
		"年龄" : 18,
		"成绩" : [88.5, 99, 58]
	}
]

json 数据类型:对象,数组,字符串,数字
对象:使用花括号{} 括起来的表示一个对象
数组:使用中括号[] 括起来的表示一个数组
字符串:使用常规双引号"" 括起来的表示一个字符串
数字:包括整形和浮点型,直接使用。

jsoncpp 库用于实现json 格式的序列化和反序列化,完成将多个数据对象组织成为json 格式字符串,以及将json格式字符串解析得到多个数据对象的功能。

jsoncpp实现序列化和反序列化主要通过三个类来实现

//Json数据对象类
class Json::Value{
Value &operator=(const Value &other); //Value重载了[]和=,因此所有的赋值和获取数据都可以通过
Value& operator[](const std::string& key);//简单的方式完成 val["姓名"] = "小明";
Value& operator[](const char* key);
Value removeMember(const char* key);//移除元素
const Value& operator[](ArrayIndex index) const; //val["成绩"][0]
Value& append(const Value& value);//添加数组元素val["成绩"].append(88);
ArrayIndex size() const;//获取数组元素个数 val["成绩"].size();
std::string asString() const;//转string string name = val["name"].asString();
const char* asCString() const;//转char* char *name = val["name"].asCString();
Int asInt() const;//转int int age = val["age"].asInt();
float asFloat() const;//转float
bool asBool() const;//转 bool
};

//json序列化类,低版本用这个更简单
class JSON_API Writer {
virtual std::string write(const Value& root) = 0;
}
class JSON_API FastWriter : public Writer {
virtual std::string write(const Value& root);
    }
class JSON_API StyledWriter : public Writer {
virtual std::string write(const Value& root);
}

//json序列化类,高版本推荐,如果用低版本的接口可能会有警告
class JSON_API StreamWriter {
virtual int write(Value const& root, std::ostream* sout) = 0;
}
class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
virtual StreamWriter* newStreamWriter() const;
}

//json反序列化类,低版本用起来更简单
class JSON_API Reader {
bool parse(const std::string& document, Value& root, bool collectComments = true);
}

//json反序列化类,高版本更推荐
class JSON_API CharReader {
virtual bool parse(char const* beginDoc, char const* endDoc,
Value* root, std::string* errs) = 0;
}
class JSON_API CharReaderBuilder : public CharReader::Factory {
virtual CharReader* newCharReader() const;
}

jsoncpp序列化

  1. 将所有的数据保存在Json::Value对象中
  2. 使用Josn::StreamWriterBulider创建一个Josn::StreamWrite
  3. 使用Josn::StreamWriter,实现序列化
class JSON_API StreamWriter {
    virtual int write(Value const& root, std::ostream* sout) = 0;
}
 
class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
    virtual StreamWriter* newStreamWriter() const;
}

StreamWriter 类的write能够将Json::Value对象转换为Json类型的字符串存放在sout中。(ostream是一个基类,可以使用std::stringstream字符串流来保存)
StremWrtier是一个抽象类不能直接定义出对象,需要先定义一个StreamWriterBuilder类的对象再通过newStreamWriter接口定义StremWrtier对象

在这里插入图片描述

jsoncpp反序列化

  1. 先定义一个Json:Value root

  2. 使用Josn::CharReaderBuilder创建一个Josn::CharReader

  3. 使用Josn::CharReader,实现序列化

class JSON_API CharReader {
    virtual bool parse(char const* beginDoc, char const* endDoc, 
                       Value* root, std::string* errs) = 0;
}
 
class JSON_API CharReaderBuilder : public CharReader::Factory {
    virtual CharReader* newCharReader() const;
}

CharReader类的parse接口能够将Json类型的字符串反序列化成Json::Value对象存放到root中。bgeinDoc是字符串的起始位置,endDoc是字符串的结束位置,errs保存错误信息
同样的,CharReader是一个抽象类,不能直接定义出对象,需要先定义一个CharReaderBuilder类的对象,再通过newCharReader接口创建一个 CharReader对象,才能使用parse接口

在这里插入图片描述

bundle文件压缩库

BundleBundle 是一个嵌入式压缩库,支持23种压缩算法和2种存档格式。使用的时候只需要加入两个文件bundle.h 和 bundle.cpp 即可。

主要使用两个接口pack和unpack来完成压缩和解压缩

 T pack( unsigned Q, T );//按照Q的压缩格式对T中的数据进行解压缩
 T unpack( T );          //将T中的数据进行解压缩

压缩格式包括:

在这里插入图片描述

文件压缩:

#include<iostream>
#include<string>
#include<fstream>
#include"bundle.h"
int main(int argc,char* argv[])
{
    std::cout<<"argv[1] 是原始文件路径名\n";
    std::cout<<"argv[2] 是压缩包名称\n";
    if(argc<3) return -1;
    std::string ifilename=argv[1];
    std::string ofilename=argv[2];
    std::ifstream ifs;
    
    ifs.open(ifilename,std::ios::binary);//打开原始文件
    
    ifs.seekg(0,std::ios::end);//跳转读写位置到文件末尾
    size_t fsize=ifs.tellg();//获取末尾偏移量(也就是获取文件大小)
    ifs.seekg(0,std::ios::beg);//跳转会起始

    std::string body;
    body.resize(fsize);
    ifs.read(&body[0],fsize);//读取文件数据到body中

    //以LZIP的方式压缩文件数据
    std::string packed=bundle::pack(bundle::LZIP,body);

    std::ofstream ofs;
    ofs.open(ofilename,std::ios::binary);//打开压缩包文件
    ofs.write(&packed[0],packed.size());//将压缩后的数据写入压缩包文件

    ifs.close();
    ofs.clear();
    return 0;
}

先打开原文件,读取源文件数据到body中,使用bundle::pack对body进行压缩,压缩后结果保存到packed,打开一个新文件,将packed中的数据写入到新文件,形成压缩文件

编译生成compress,将bundle.cpp压缩到bundle.cpp.lz

在这里插入图片描述

文件解压

#include<iostream>
#include<string>
#include<fstream>
#include"bundle.h"
int main(int argc,char* argv[])
{
    if(argc<3)
    {
    std::cout<<"argv[1] 是压缩包名称\n";
    std::cout<<"argv[2] 是解压后的文件名\n";   
    return -1;
    }
    std::string ifilename=argv[1];
    std::string ofilename=argv[2];
    std::ifstream ifs;
    ifs.open(ifilename,std::ios::binary);
    ifs.seekg(0,std::ios::end);
    size_t fsize=ifs.tellg();
    ifs.seekg(0,std::ios::beg);

    std::string body;
    body.resize(fsize);
    ifs.read(&body[0],fsize);
    ifs.close();

    std::string unpacked=bundle::unpack(body);
    std::ofstream ofs;
    ofs.open(ofilename,std::ios::binary);
    ofs.write(&unpacked[0],unpacked.size());
    ofs.close();
    return 0;
}

先打开压缩包文件,将压缩包文件数据读取到body,使用bundle::unpask对body进行解压缩,解压结果保存到unpacked,打开一个新文件将unpacked中数据写入新文件,形成解压后文件

编译生成uncompress,将先前压缩的bundle.cpp.lz解压到bundle2.cpp

在这里插入图片描述

对比bundle.cpp和bundle2.cpp的MD5值相等

在这里插入图片描述

bundle.cpp生成静态库

bundle.cpp文件太大每次编译时间长,可以考虑将bundle.cpp生成静态库

g++ -c bundle.cpp bundle.o
ar -cr libbundle.a bundle.o

将libbundle.a添加到lib目录,编译时添加选项-L./lib -lbundle

httplib库

httplib 库,一个C++11 单文件头的跨平台HTTP/HTTPS 库。安装起来非常容易。只需包含httplib.h 在你的代码中即可。
httplib 库实际上是用于搭建一个简单的http 服务器或者客户端的库,这种第三方网络库,可以让我们免去搭建服务器或客户端的时间,把更多的精力投入到具体的业务处理中,提高开发效率。

httplib库Request类

http请求格式

首行:[方法]+[url]+[版本]

Header:请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束

Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个
Content-Length属性来标识Body的长度;

在这里插入图片描述

Request类的功能

  • 客户端保存所有http请求相关信息,最终组织http请求发送给服务器
  • 服务器收到http请求后及进行解析,将解析的数据保存在request结构体中,等待后续处理
 //文件信息结构体
    struct MultipartFormData {
        std::string name;        //字段名称
        std::string content;     //文件内容
        std::string filename;    //文件名称
        std::string content_type;//文件类型
   };
  using MultipartFormDataItems=std::vector<MultipartFormData>;
//Request结构体
struct Request {
	std::string method;//请求方法
	std::string path;//url
	Headers headers;//头部字段
	std::string body;//正文
	// for server
	std::string version;//协议版本
	Params params;//查询字符串
	MultipartFormDataMap files;//保存客户端上传的文件信息
	Ranges ranges;//用于实现断点重传的请求文件区间
    
	bool has_header(const char *key) const;//查询请求报头有无某个头部字段
	std::string get_header_value(const char *key, size_t id = 0) const; //获取请求报头中对应头部字段的值
	void set_header(const char *key, const char *val);//设置头部字段的值
	bool has_file(const char *key) const;//判定是否有对应的文件
	MultipartFormData get_file_value(const char *key) const;//获取对应的文件信息
};

httplib库Response类

http响应格式

首行: [版本号] + [状态码] + [状态码解释]
Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在body中.

在这里插入图片描述

Response类的功能

  • 用户将响应数据放到结构体中,httplib会将其中的数据按照http响应格式组织成为http响应,发送给客户端。
 struct Response {
        std::string version;   //协议版本号 http1.1
        int status = -1;       //响应状态码,
        std::string reason;    
        Headers headers;       //响应报头
        std::string body;      //响应给客户端的正文
        std::string location; // 重定向位置
 
		//以key-val将相应的字段设定到响应报头中
		void set_header(const char *key, const char *val);//设置头部字段
        void set_content(const std::string &s, const char *content_type);//设置正文
   };

httplib中的Server类

Server类的功能

用于搭建http服务器

class Server {
        //Handler一个函数指针,业务处理是涉及到的回调函数
        using Handler = std::function<void(const Request &, Response &)>;
        //Handlers是一个映射表,请求与处理函数映射表
        using Handlers = std::vector<std::pair<std::regex, Handler>>;
        
        std::function<TaskQueue *(void)> new_task_queue;//线程池,用于处理请求
    
        //将Get/Post...方法的请求资源与处理函数设置到Handlers表中
        Server &Get(const std::string &pattern, Handler handler);
        Server &Post(const std::string &pattern, Handler handler);
        Server &Put(const std::string &pattern, Handler handler);
        Server &Patch(const std::string &pattern, Handler handler);  
        Server &Delete(const std::string &pattern, Handler handler);
        Server &Options(const std::string &pattern, Handler handler);
        
 
        //搭建并启动服务器
        bool listen(const char *host, int port, int socket_flags = 0);
 }

Handler:函数指针,http请求处理回调函数格式
httplib搭建的服务器收到请求后,进行解析,得到一个request结构体,其中包含了请求数据,
根据请求数据我们就可以处理这个请求了,这个处理函数定义的格式就是Handler格式

request参数:保存请求数据,让用户能够根据请求数据进行业务处理
response参数:需要用户在业务处理中,填充数据,最终要相应给客户端

Handlers:是一个请求路由数组std::vector<std::pair<std::regex, Handler>>;包含两个信息

regex:正则表达式-用于匹配http请求资源路径

Handler:请求处理函数指针

可以理解为:**handlers是一张表,映射了一个客户端请求的资源路径和一个处理函数(用户自己定义的)**当服务器收到请求解析得到Request就会根据资源路径以及请求方法到handlers表中查找是否有对饮的处理函数,如果有则调用函数进行处理,没有返回404。handlers表决定哪个请求用哪个函数

ew_task_queue: 线程池,用于处理http请求,httplib收到一个新建连接,则将新的客户端连接抛入线程池中

线程池中线程的工作:

  1. 接受请求,解析请求,得到Request结构体也就是请求的数据
  2. 在handlers映射表中根据请求信息查找处理函数,调用函数进行处理
  3. 处理完毕,根据函数返回的Response结构体中的数据组织http响应并发送给客户端

httplib中的Client类

class Client {
	Client(const std::string &host, int port);//构造函数,传入服务器ip和端口
	Result Get(const char *path, const Headers &headers);//向服务器发送GET请求
	Result Post(const char *path, const char *body, size_t content_length,const char *content_type); //向服务器发送Post请求
	Result Post(const char *path, const MultipartFormDataItems &items);//Post请求提交多区域数据,用于多文件上传
}

httplib库搭建简单的服务器

#include"httplib.h"


void Hello(const httplib::Request& req, httplib::Response& rsp)
{
    rsp.set_content("Hello world!!","text/plain");//设置正文
    rsp.status=200;//设置响应状态码
}
void Numbers(const httplib::Request& req, httplib::Response& rsp)
{
    auto num=req.matches[1];//0里边保存的是整体的path,往后的下标保存的是捕捉的数据
    rsp.set_content(num,"text/plain");//设置正文
    rsp.status=200;//设置响应状态码
}
void Multipart(const httplib::Request& req, httplib::Response& rsp)
{
    auto ret=req.has_file("file");
    if(ret==false)
    {
        std::cout<<"not file upload\n";
        rsp.status=400;
        return;
    }
    const auto& file=req.get_file_value("file");
    rsp.body.clear();
    rsp.body=file.filename;
    rsp.body+='\n';
    rsp.body+=file.content;
    rsp.set_header("Content-Type","text/plain");
    rsp.status=200;
    return ;
}
int main()
{
    httplib::Server server;//实例化server对象

    server.Get("/hi",Hello);//注册一个针对/hi的Get请求的梳理函数Hello到Handlers映射表
    server.Get(R"(/numbers/(\d+))",Numbers);
    server.Post("multipart",Multipart);
    server.listen("0.0.0.0",9090);
    return 0;
}

实例化server对象,在Handler表结构中注册相应请求资源的处理函数,listen启动服务器

httplib库搭建简单的服务器

#include"httplib.h"    
#define IP "43.143.228.57"    
#define PORT 8080    
using namespace std;    
    
int main(){    
  //建立客户端对象    
  httplib::Client client(IP,PORT); 
  //单个文件信息组织   
  httplib::MultipartFormData file;    
  file.name="file"; //字段名       
  file.content="hello world";    //文件内容
  file.filename="Hello.txt";    //文件名
  file.content_type="text/plain";    //文件类型
  
  //MultipartFormDataItems 文件信息数组
  httplib::MultipartFormDataItems items;    
  items.push_back(file);    
  //请求服务器上/file资源,发送item文件集合给服务器
  auto res=client.Post("/multipart",items);     
  cout<<res->status<<endl;
  cout<<res->body<<endl;
  return 0;                                                                                                                                 
}    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lilvic

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值