文章目录
- 1. jsoncpp库接口使用测试
- 2. bundle数据压缩库使用测试
- 3. httplib库API使用测试
- httplib库Request类
- httplib库Response类
- httplib库Sever类
- httplib库Client类
- 4. httplib库快速搭建Http客户端服务器
- http服务器代码
- 服务器运行截图
- 华为云服务器开放端口
- http客户端代码
- 客户端运行截图
- 4. 库测试代码位置
1. jsoncpp库接口使用测试
jsoncpp库使用步骤;
- 序列化
- 将需要json化的数据保存在json::Value中
- 使用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;
}
- 反序列化
- jsoncpp中的分序列化,是把json数据字符串放到 Json::Value对象中,取数据在 Json::Value上
- 使用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实现文件压缩与解压缩案例
- 压缩案例:
#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;
}
因为库文件比较大,编译时间会比较长,警告忽视即可
- 解压案例
#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类作用:
- 客户端保存所有的Http请求相关信息,最终组织http请求发送给服务器
- 服务器收到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 库测试代码