Linux(CentOS)/Windows-C++ 云备份项目(jsoncpp库,bundle数据压缩库API测试,httplib库快速搭建Http服务器,客户端测试,华为云服务器开放端口)

1. jsoncpp库接口使用测试

jsoncpp库使用步骤;

  • 序列化
    1. 将需要json化的数据保存在json::Value中
    2. 使用Json::StreamWriter,Json::StreamWriterBuilder完成序列化
#include <iostream>
#include <sstream>
#include <string>
#include <memory>
#include <jsoncpp/json/json.h>
int main(int argc, char const *argv[])
{
    const char *name = "小米";
    int age = 20;
    float score[] = {77.5, 87.5, 90};
    Json::Value value;
    value["姓名"] = name;
    value["年龄"] = age;
    value["成绩"].append(score[0]);
    value["成绩"].append(score[1]);
    value["成绩"].append(score[2]);
    Json::StreamWriterBuilder builder;
    std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
    std::stringstream ss;
    writer->write(value, &ss);
    std::cout << ss.str() << std::endl;
    return 0;
}

在这里插入图片描述

  • 反序列化
    1. jsoncpp中的分序列化,是把json数据字符串放到 Json::Value对象中,取数据在 Json::Value上
    2. 使用CharReaderBuilder CharReader类来实现反序列化
#include <iostream>
#include <sstream>
#include <string>
#include <memory>
#include <jsoncpp/json/json.h>
using namespace std;

int main(int argc, char const *argv[])
{
    string str = R"({"姓名":"小米","年龄":19,"成绩":[77.5,87.5,90]})"; // c++11语法R"(str)"
    Json::Value value;
    Json::CharReaderBuilder builder;
    std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
    string errs;
    bool ok = reader->parse(str.c_str(), str.c_str() + str.size(), &value, &errs);
    if (!ok)
    {
        std::cout << "parse error: " << errs << std::endl;
    }
    else
    {
        std::cout << "姓名: " << value["姓名"].asString() << std::endl;
        std::cout << "年龄: " << value["年龄"].asInt() << std::endl;
        std::cout << "成绩: " << value["成绩"][0].asFloat() << std::endl;
        std::cout << "成绩: " << value["成绩"][1].asFloat() << std::endl;
        std::cout << "成绩: " << value["成绩"][2].asFloat() << std::endl;
        for(int i=0;i<value["成绩"].size();i++){

            std::cout<<value["成绩"][i]<<" ";
        }
    }
    return 0;
}

在这里插入图片描述

2. bundle数据压缩库使用测试

biundle库是一个嵌入代码式的压缩库,支持23种压缩算法和两种存档格式,使用时只需要加入bundle.h和bundle.cpp即可

  • bundle实现文件压缩与解压缩案例
  1. 压缩案例:
#include <iostream>
#include <string>
#include <fstream>
#include "../bundle/bundle.h"
using namespace std;
int main(int argc, char const *argv[])
{
    cout << "argv[1]是原始文件名称" << endl;
    cout << "argv[2]是压缩后的文件名称" << endl;
    if (argc < 3)
    {
        cout << "参数错误" << endl;
        return 1;
    }
    string filename = argv[1];
    string outname = argv[2];
    ifstream file;
    file.open(filename, std::ios::binary); // 二进制打开
    file.seekg(0, std::ios::end);          // 跳转到文件末尾,方便获取文件大小
    size_t f_size = file.tellg();
    file.seekg(0, std::ios::beg); // 跳转到文件开头
    string body;
    body.resize(f_size);
    file.read(&body[0], f_size); // 文件内容读入body &body[0]为字符串首地址
    file.close();
    string packed = bundle::pack(bundle::LZIP, body); // 压缩后的数据,压缩格式为LZIP
    ofstream outfile;
    outfile.open(outname, std::ios::binary);
    outfile.write(packed.c_str(), packed.size());
    outfile.close();
    cout << "压缩完成" << endl;
    return 0;
}

在这里插入图片描述

因为库文件比较大,编译时间会比较长,警告忽视即可

  1. 解压案例
#include <iostream>
#include <fstream>
#include <string>
#include "../bundle/bundle.h"
using namespace std;
int main(int argc, char const *argv[])
{
    cout << "argv[1]是压缩包名称" << endl;
    cout << "argv[2]是解压缩后的文件名称" << endl;
    if (argc < 3)
    {
        cout << "参数错误" << endl;
        return 1;
    }
    string infile = argv[1];
    string outfile = argv[2];
    ifstream inf;
    inf.open(infile, std::ios::binary);
    inf.seekg(0, std::ios::end);
    int size = inf.tellg();
    inf.seekg(0, std::ios::beg);
    string body;
    body.resize(size);
    inf.read(&body[0], size);
    inf.close();
    string unpacked;
    unpacked = bundle::unpack(body); // 解压
    ofstream outf;
    outf.open(outfile, std::ios::binary);
    outf.write(unpacked.c_str(), unpacked.size());
    outf.close();
    cout << "解压缩成功" << endl;
    return 0;
}

在这里插入图片描述
解压后的文件大小相同,计算文件的md5值看这两个文件是否相同
在这里插入图片描述
md5值完全相同

3. httplib库API使用测试

httplib库是一个C++11的单文件跨平台HTTP/HTTPS库,httplib库实际上是用于搭建一个简单http服务器的库,可以让我们免去搭建客户端服务器的时间。当然这个库也可以使用我的另一个项目HttpSever的部分代码,这里为了提升开发效率所以使用第三方库
HttpSever gitub代码
做过这个项目这个库的学习就会十分轻松了

httplib库Request类

httplib库Request类部分主要的源代码解析:

struct MultipartFormData {
  std::string name; //字段名称
  std::string content; //文件内容
  std::string filename; //文件名称
  std::string content_type; //正文类型
};

struct Request {
  std::string method; //请求方法
  std::string path;   //请求uri
  Headers headers;    //请求头部字段
  std::string body;   //请求正文

  // for server
  std::string version; //协议版本
  Params params;       //保存查询字符串
  MultipartFormDataMap files; //保存客户端上传的文件信息 key value型
  Ranges ranges;  //实现断点续传的请求文件区间

  bool has_header(const std::string &key) const; //查询是否有某个头部字段
  std::string get_header_value(const std::string &key, size_t id = 0) const; //获取头部字段的值
  uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; 
  size_t get_header_value_count(const std::string &key) const; 
  void set_header(const std::string &key, const std::string &val); //设置头部字段的值

  bool has_file(const std::string &key) const; //根据MultipartFormData中的name字段查找文件是否存在
  MultipartFormData get_file_value(const std::string &key) const; //获取其中某一个文件信息
  std::vector<MultipartFormData> get_file_values(const std::string &key) const;
};

httplib库Request类作用:

  1. 客户端保存所有的Http请求相关信息,最终组织http请求发送给服务器
  2. 服务器收到http请求之后进行解析,将解析数据保存在Request结构体上,待后续处理

httplib库Response类

httplib库Response类部分主要的源代码解析:

struct Response {
  std::string version; //协议版本
  int status = -1; //响应状态码
  std::string reason;
  Headers headers; //响应头部字段
  std::string body; //响应正文
  std::string location; // 重定向位置

  void set_header(const std::string &key, const std::string &val); //设置头部字段
  
  void set_content(const char *s, size_t n, const std::string &content_type); //设置正文
  void set_content(const std::string &s, const std::string &content_type);
  void set_content(std::string &&s, const std::string &content_type);
  
  Response() = default;
  Response(const Response &) = default;
  Response &operator=(const Response &) = default;
  Response(Response &&) = default;
  Response &operator=(Response &&) = default;
  ~Response() {
    if (content_provider_resource_releaser_) {
      content_provider_resource_releaser_(content_provider_success_);
    }
  }
};

Response结构体功能:用户将响应数据放入结构中,httplib会将数据按照http响应格式组织成为http响应

httplib库Sever类

httplib库Sever类部分源码解析:

class Server {
public:
  using Handler = std::function<void(const Request &, Response &)>; //函数指针类型,定义了请求处理回调函数格式
  /**
  	 httplib搭建的服务器收到请求后,进行解析,得到Request结构体,其中包含了请求数据
	 
	 根据请求数据处理请求,这个处理函数定义的格式就是Handler格式 void(const Request &, Response &)
	 
	 Request参数,保存请求数据,让用户能根据请求数据进行业务处理
	 
	 Response参数,需要用户在业务处理填充数据,最终发送给响应给客户端
  */
  using Handlers =
     std::vector<std::pair<std::regex, Handler>>;//请求与处理函数映射表,那个函数用哪个函数处理
  /**
  	 regex:正则表达式,用来匹配http请求资源路径
  	 
  	 Handler:请求处理函数指针
  	 
  	 Handlers:是一张表,映射了客户端请求的资源路径和一个处理函数,

	 当服务器收到请求解析得到Request就会根据资源路径以及请求方法到这张表去查
	 如果有:调用这个函数进行处理
	 如果没有:响应404
  */
  std::function<TaskQueue *(void)> new_task_queue; //线程池,用于处理请求
  /**
  	 httplib收到一个新建链接,则将新客户端链接抛入线程池中
  	 线程池中的线程工作:
  	 	1. 接受数据,解析请求,得到Request结构体的数据
  	 	2. 在Handlers映射表中查找处理函数,如果有则调用,否则响应404
  	 	3. 当处理回调函数调用完毕,根据函数返回的Response结构体中的数据组织http响应发送给客户端
  */
  //针对各种请求方法设定映射的处理函数,eg Get("/hello",hello/**(函数指针)*/) 会将Handlers映射表中添加新的映射
  Server &Get(const std::string &pattern, Handler handler);
  Server &Post(const std::string &pattern, Handler handler);
  Server &Post(const std::string &pattern, HandlerWithContentReader handler);
  Server &Put(const std::string &pattern, Handler handler);
  Server &Put(const std::string &pattern, HandlerWithContentReader handler);
  Server &Patch(const std::string &pattern, Handler handler);
  Server &Patch(const std::string &pattern, HandlerWithContentReader handler);
  Server &Delete(const std::string &pattern, Handler handler);
  Server &Delete(const std::string &pattern, HandlerWithContentReader handler);
  Server &Options(const std::string &pattern, Handler handler);

  bool listen(const std::string &host, int port, int socket_flags = 0);//启动http服务器
};

Sever类功能:用来搭建Http服务器

httplib库Client类

httplib库Client类部分源码解析:

class Client {
public:
  // HTTP only interface
  explicit Client(const std::string &host, int port); //传入服务器ip地址和端口
  Result Get(const std::string &path, const Headers &headers); //向服务器发送GET请求
  Result Post(const std::string &path, const char *body, size_t content_length,
              const std::string &content_type); //向服务器提供POST请求
  Result Post(const std::string &path, const Headers &headers,
              const MultipartFormDataItems &items); //items:是文件信息 post提交多区域数据,常见于多文件上传
};

4. httplib库快速搭建Http客户端服务器

http服务器代码

Server代码

#include "../httplib/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 之后的下标中保存的是正则表达式捕捉的数据,就是uri /number/[i]的i
    rsp.set_content(num, "text/plain"); // 设置响应正文
    rsp.status = 200;
}
void Mutipart(const httplib::Request &req, httplib::Response &rsp)
{
    auto ret = req.has_file("file");
    if (ret == false)
    {
        std::cout << "不是文件上传\n"; // 通常 Mutipart用于上传数据
        rsp.status = 404;
    }
    else
    {
        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;
    }
}
int main(int argc, char const *argv[])
{
    httplib::Server svr;                   // 实例化Sever来搭建服务器
    svr.Get("/hi", Hello);                 // 注册一个针对/hi的GET请求映射函数,处理方法为Hello的回调函数
    svr.Get(R"(/numbers/(\d+))", Numbers); // 正则表达式/numbers/1 /numbers/2 .... 都会响应这个方法
    svr.Post("/multipart", Mutipart);
    svr.listen("0.0.0.0", 8081); // 匹配服务器所有网卡
    return 0;
}

服务器运行截图

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

运行失败的,虚拟机记得关闭防火墙。云服务器记得开放端口

华为云服务器开放端口

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

http客户端代码

Client代码

#include "../httplib/httplib.h"
#define SEVER_IP "116.204.70.147"
#define SEVER_PORT 8081
int main(int argc, char const *argv[])
{
    httplib::Client client(SEVER_IP, SEVER_PORT);
    // 区域文件结构体
    struct httplib::MultipartFormData item;
    item.name = "file"; // 和服务器 /multipart post方法注册函数方法中处理的文件名一致
    item.filename = "hello.txt";
    item.content = "hello world"; // 这个文件的内容,为了省事,正常情况这个文件内容需要io读取
    item.content_type = "text/plain";
    httplib::MultipartFormDataItems items; // 一次性可以上传多个文件数据,本质是MultipartFormData数组
    items.push_back(item);
    httplib::Result response = client.Post("/multipart", items); // 与服务器注册的uri一致 ,向服务器发送POST请求
    std::cout << "server response.status: " << response->status << std::endl;
    std::cout << "server response.body: " << response->body << std::endl;
    return 0;
}

客户端运行截图

在这里插入图片描述

4. 库测试代码位置

Gitee 库测试代码
Github 库测试代码

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

NUC_Dodamce

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

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

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

打赏作者

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

抵扣说明:

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

余额充值