一.脚手架介绍
1.概念

2.作用

3.封装






a.基础功能库

b.中间件及客户端SDK

二.虚拟机环境安装(没有公网地址,只能自己主机访问)
1.Vmware
a.软件下载
软件下载链接:https://www.vmware.com/products/desktop-hypervisor/workstation-and-fusion










b.软件安装



2.Ubuntu22.04
a.镜像下载
国内源:https://mirrors.163.com/ubuntu-releases/




b.虚拟机安装
































c.虚拟机基础配置


我们可以通过我们的xshell 和 我们的trae来进行连接
三.Docker介绍与安装
1.docker介绍


我们要部署多个服务器,就直接拉取docker容器即可

2.docker安装

sudo apt update
sudo apt-get install ca-certificates curl gnupg lsb-release
sudo apt-get install make gcc g++ libz-dev lrzsz vim software-properties-common

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerhub.timeweb.cloud",
"https://docker.m.daocloud.io",
"https://docker.mirrors.ustc.edu.cn",
"https://b1skj1q5.mirror.aliyuncs.com",
"https://docker.m.daocloud.io",
"https://dockerhub.timeweb.cloud",
]
}
EOF
sudo systemctl daemon-reload

curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
sudo apt update
sudo apt-get install docker-ce docker-ce-cli containerd.io

sudo curl -L "https://github.com/docker/compose/releases/download/v2.13.0/docker-composelinux-x86_64" -o /usr/bin/docker-compose
sudo chmod +x /usr/bin/docker-compose
docker-compose --version
sudo groupadd docker
sudo gpasswd -a $USER docker
newgrp docker
docker version
3.docker常用指令
---------------------后续更新------------------
四.gflags介绍
1.gflags介绍

官方文档:https://gflags.github.io/gflags/
代码仓库:https://github.com/gflags/gflags.git
2.gflags安装




3.gflags使用






4.入门案例




a.main.cc
cpp
/*
gflags使用:
1. 包含头文件 <gflags/gflags.h>
2. 定义要捕获的参数信息,以及设置默认值
3. 解析参数,初始化参数数据
4. 访问参数
*/
// 一.
#include <gflags/gflags.h>
#include <iostream>
#include <string>
// 二.
DEFINE_bool(reuse_addr,true,"使用启用地址重用选项");
DEFINE_int32(listen_port,8080,"服务器监听端口");
DEFINE_string(listen_ip,"0.0.0.0","服务器监听IP");
DEFINE_double(pi,3.14,"圆周率");
int main(int argc,char* argvp[])
{
// 三.
google::ParseCommandLineFlags(&argc,&argvp,true);
// 四.
std::cout << "reuse_addr: " << FLAGS_reuse_addr << std::endl;
std::cout << "listen_port: " << FLAGS_listen_port << std::endl;
std::cout << "listen_ip: " << FLAGS_listen_ip << std::endl;
std::cout << "pi: " << FLAGS_pi << std::endl;
return 0;
}
b.makefile
bash
main:main.cc
g++ main.cc -o main -lgflags
.PHONY:clean
clean:
rm -rf main
如果直接进行运行,就是我们对应的默认值

写入那个参数就修改那个参数

c.main.conf
我们可以新增一个conf文件,里面写的就是我们的--listen_ip等信息
配置文件里面不要加""(双引号)
cpp
--listen_ip=127.0.0.1
--listen_port=8080
--pi=3.1415926
--reuse_addr=false

d.外部文件访问(child.cc)
cpp
#include <iostream>
#include <gflags/gflags.h>
DECLARE_string(listen_ip);
DECLARE_bool(reuse_addr);
DECLARE_int32(listen_port);
DECLARE_double(pi);
void print()
{
std::cout << "呵呵" << std::endl;
std::cout << "reuse_addr: " << FLAGS_reuse_addr << std::endl;
std::cout << "listen_port: " << FLAGS_listen_port << std::endl;
std::cout << "listen_ip: " << FLAGS_listen_ip << std::endl;
std::cout << "pi: " << FLAGS_pi << std::endl;
}
makefile
bash
main : main.cc
g++ main.cc child.cc -o main -lgflags
.PHONY : clean
clean:
rm -rf main


五.gtest的使用
1.介绍以及安装


2.使用




cpp
/*
gtest简单使用
1.包含头文件 <gtest/gtest.h>
2.定义测试用例
3.初始化测试环境
4.执行测试用例
*/
#include <gtest/gtest.h>
#include <iostream>
#include <unordered_map>
TEST(unordered_map_test,insert)
{
std::unordered_map<int,int> map;
map.insert(std::make_pair(1,1));
map.insert(std::make_pair(2,2));
map.insert(std::make_pair(3,3));
ASSERT_EQ(map.size(),3);
}
TEST(unordered_map_test,find)
{
std::unordered_map<int,int> map;
map.insert(std::make_pair(1,1));
map.insert(std::make_pair(2,2));
ASSERT_NE(map.find(2),map.end());
ASSERT_EQ(map.find(1)->second,1);
}
int main(int argc,char* argv[])
{
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}
bash
simple:simple.cc
g++ -o $@ $^ -lgtest
.PHONY:clean
clean:
rm -rf simple

3.gtest事件机制




a.全局事件

cpp
/*
全局测试套件使用:
1. 包含头文件 <gtest/gtest.h>
2. 定义全局测试环境类
1. 定义测试环境初始接口:会在所有用例执行前执行
2. 定义测试环境清理接口:会在所有用例执行后执行
3. 定义测试用例
4. 初始化测试环境
5. 执行测试用例
*/
#include <gtest/gtest.h>
#include <iostream>
#include <unordered_map>
class GlobalTestEnvironment : public testing::Environment {
public:
virtual void SetUp() {
std::cout << "执行于所有用例之前" << std::endl;
}
virtual void TearDown() {
std::cout << "执行于所有用例之后" << std::endl;
}
};
TEST(unordered_map_test,insert)
{
std::unordered_map<int,int> map;
map.insert(std::make_pair(1,1));
map.insert(std::make_pair(2,2));
map.insert(std::make_pair(3,3));
ASSERT_EQ(map.size(),3);
}
TEST(unordered_map_test,find)
{
std::unordered_map<int,int> map;
map.insert(std::make_pair(1,1));
map.insert(std::make_pair(2,2));
ASSERT_NE(map.find(2),map.end());
ASSERT_EQ(map.find(1)->second,1);
}
int main(int argc,char* argv[])
{
testing::InitGoogleTest(&argc,argv);
testing::AddGlobalTestEnvironment(new GlobalTestEnvironment);
return RUN_ALL_TESTS();
}

cpp
// /*
// 全局测试套件使用:
// 1. 包含头文件 <gtest/gtest.h>
// 2. 定义全局测试环境类
// 1. 定义测试环境初始接口:会在所有用例执行前执行
// 2. 定义测试环境清理接口:会在所有用例执行后执行
// 3. 定义测试用例
// 4. 初始化测试环境
// 5. 执行测试用例
// */
// #include <gtest/gtest.h>
// #include <iostream>
// #include <unordered_map>
// class GlobalTestEnvironment : public testing::Environment {
// public:
// virtual void SetUp() {
// std::cout << "执行于所有用例之前" << std::endl;
// }
// virtual void TearDown() {
// std::cout << "执行于所有用例之后" << std::endl;
// }
// };
// TEST(unordered_map_test,insert)
// {
// std::unordered_map<int,int> map;
// map.insert(std::make_pair(1,1));
// map.insert(std::make_pair(2,2));
// map.insert(std::make_pair(3,3));
// ASSERT_EQ(map.size(),3);
// }
// TEST(unordered_map_test,find)
// {
// std::unordered_map<int,int> map;
// map.insert(std::make_pair(1,1));
// map.insert(std::make_pair(2,2));
// ASSERT_NE(map.find(2),map.end());
// ASSERT_EQ(map.find(1)->second,1);
// }
// int main(int argc,char* argv[])
// {
// testing::InitGoogleTest(&argc,argv);
// testing::AddGlobalTestEnvironment(new GlobalTestEnvironment);
// return RUN_ALL_TESTS();
// }
// -----------------------------------------------------------------------------
/*
全局测试套件使用:
1. 包含头文件 <gtest/gtest.h>
2. 定义全局测试环境类
1. 定义测试环境初始接口:会在所有用例执行前执行
2. 定义测试环境清理接口:会在所有用例执行后执行
3. 定义测试用例
4. 初始化测试环境
5. 执行测试用例
*/
#include <gtest/gtest.h>
#include <iostream>
#include <unordered_map>
std::unordered_map<int,int> map;
class GlobalTestEnvironment : public testing::Environment {
public:
virtual void SetUp() {
std::cout << "执行于所有用例之前" << std::endl;
map.insert(std::make_pair(1,1));
map.insert(std::make_pair(2,2));
map.insert(std::make_pair(3,3));
}
virtual void TearDown() {
std::cout << "执行于所有用例之后" << std::endl;
}
};
TEST(GlobalTestEnvironment,remove)
{
map.erase(2);
}
TEST(GlobalTestEnvironment,size)
{
ASSERT_EQ(map.size(),3);
}
TEST(GlobalTestEnvironment,find)
{
ASSERT_NE(map.find(2),map.end());
ASSERT_EQ(map.find(1)->second,1);
}
int main(int argc,char* argv[])
{
testing::InitGoogleTest(&argc,argv);
testing::AddGlobalTestEnvironment(new GlobalTestEnvironment);
return RUN_ALL_TESTS();
}

b.TestSuite事件


cpp
// /*
// 全局测试套件使用:
// 1. 包含头文件 <gtest/gtest.h>
// 2. 定义全局测试环境类
// 1. 定义测试环境初始接口:会在所有用例执行前执行
// 2. 定义测试环境清理接口:会在所有用例执行后执行
// 3. 定义测试用例
// 4. 初始化测试环境
// 5. 执行测试用例
// */
// #include <gtest/gtest.h>
// #include <iostream>
// #include <unordered_map>
// class GlobalTestEnvironment : public testing::Environment {
// public:
// virtual void SetUp() {
// std::cout << "执行于所有用例之前" << std::endl;
// }
// virtual void TearDown() {
// std::cout << "执行于所有用例之后" << std::endl;
// }
// };
// TEST(unordered_map_test,insert)
// {
// std::unordered_map<int,int> map;
// map.insert(std::make_pair(1,1));
// map.insert(std::make_pair(2,2));
// map.insert(std::make_pair(3,3));
// ASSERT_EQ(map.size(),3);
// }
// TEST(unordered_map_test,find)
// {
// std::unordered_map<int,int> map;
// map.insert(std::make_pair(1,1));
// map.insert(std::make_pair(2,2));
// ASSERT_NE(map.find(2),map.end());
// ASSERT_EQ(map.find(1)->second,1);
// }
// int main(int argc,char* argv[])
// {
// testing::InitGoogleTest(&argc,argv);
// testing::AddGlobalTestEnvironment(new GlobalTestEnvironment);
// return RUN_ALL_TESTS();
// }
// -----------------------------------------------------------------------------
/*
全局测试套件使用:
1. 包含头文件 <gtest/gtest.h>
2. 定义全局测试环境类
1. 定义测试环境初始接口:会在所有用例执行前执行
2. 定义测试环境清理接口:会在所有用例执行后执行
3. 定义测试用例
4. 初始化测试环境
5. 执行测试用例
*/
#include <gtest/gtest.h>
#include <iostream>
#include <unordered_map>
std::unordered_map<int,int> map;
class GlobalTestEnvironment : public testing::Environment {
public:
virtual void SetUp() {
std::cout << "执行于所有用例之前" << std::endl;
map.insert(std::make_pair(1,1));
map.insert(std::make_pair(2,2));
map.insert(std::make_pair(3,3));
}
virtual void TearDown() {
std::cout << "执行于所有用例之后" << std::endl;
map.clear();
}
};
TEST(GlobalTestEnvironment,remove)
{
map.erase(2);
}
TEST(GlobalTestEnvironment,size)
{
ASSERT_EQ(map.size(),3);
}
TEST(GlobalTestEnvironment,find)
{
ASSERT_NE(map.find(2),map.end());
ASSERT_EQ(map.find(1)->second,1);
}
int main(int argc,char* argv[])
{
testing::InitGoogleTest(&argc,argv);
testing::AddGlobalTestEnvironment(new GlobalTestEnvironment);
return RUN_ALL_TESTS();
}


cpp
/*
局部测试套件使用:
1. 包含头文件 <gtest/gtest.h>
2. 定义测试套件
1. 定义测试套件初始接口:会在每个测试用例执行之前执行
2. 定义测试套件清理接口:会在每个测试用例执行之后执行
3. 定义测试用例
4. 执行测试用例
*/
#include <gtest/gtest.h>
#include <iostream>
#include <unordered_map>
class LocalTestEnvironment : public ::testing::Test
{
public:
std::unordered_map<int,int> map;
virtual void SetUp() {
std::cout << "执行于每个用例之前" << std::endl;
map.insert(std::make_pair(1,1));
map.insert(std::make_pair(2,2));
map.insert(std::make_pair(3,3));
}
virtual void TearDown() {
std::cout << "执行于每个用例之后" << std::endl;
map.clear();
}
};
TEST_F(LocalTestEnvironment,find)
{
ASSERT_EQ(map.size(),3);
ASSERT_EQ(map.find(1)->second,1);
map.erase(1);
std::cout << "测试用例1" << std::endl;
}
TEST_F(LocalTestEnvironment,remove)
{
map.erase(1);
ASSERT_EQ(map.size(),2);
std::cout << "测试用例2" << std::endl;
}
int main(int argc,char* argv[])
{
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}

c.TestCase事件

cpp
/*
局部测试套件使用:
1. 包含头文件 <gtest/gtest.h>
2. 定义测试套件
1. 定义测试套件初始接口:会在每个测试用例执行之前执行
2. 定义测试套件清理接口:会在每个测试用例执行之后执行
3. 定义测试用例
4. 执行测试用例
*/
#include <gtest/gtest.h>
#include <iostream>
#include <unordered_map>
class LocalTestEnvironment : public ::testing::Test
{
public:
std::unordered_map<int,int> map;
static void SetUpTestCase()
{
std::cout << "公共环境接口,所有用例执行之前执行" << std::endl;
}
static void TearDownTestCase()
{
std::cout << "公共环境接口,所有用例执行之后执行" << std::endl;
}
virtual void SetUp() {
std::cout << "执行于每个用例之前" << std::endl;
map.insert(std::make_pair(1,1));
map.insert(std::make_pair(2,2));
map.insert(std::make_pair(3,3));
}
virtual void TearDown() {
std::cout << "执行于每个用例之后" << std::endl;
map.clear();
}
};
TEST_F(LocalTestEnvironment,find)
{
ASSERT_EQ(map.size(),3);
ASSERT_EQ(map.find(1)->second,1);
map.erase(1);
std::cout << "测试用例1" << std::endl;
}
TEST_F(LocalTestEnvironment,remove)
{
map.erase(1);
ASSERT_EQ(map.size(),2);
std::cout << "测试用例2" << std::endl;
}
int main(int argc,char* argv[])
{
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}
SetUpTestCase 和 TearDownTestCase 是公共的接口,所有的环境都能进行使用

六.spdlog介绍
1.介绍

2.特点

3.安装

4.使用

















a.标准输出
cpp
/*
标准输出的日志:
1. 包含头文件 <spdlog/spdlog.h>
2. 创建日志对象
3. 设置日志输出等级
4. 设置日志输出格式
5. 输出日志
*/
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <iostream>
int main(int argc, char *argv[])
{
auto logger = spdlog::stdout_color_mt("stdout");
logger->set_level(spdlog::level::debug);
logger->set_pattern("[%H:%M:%S][%7l]: %v");
logger->debug("{} 今年 {}岁","小明",18);
logger->info("{} 今年 {}岁","小红",19);
logger->warn("{} 今年 {}岁","小刚",20);
logger->error("{} 今年 {}岁","小绿",21);
return 0;
}
bash
stdout : stdout.cc
g++ -o $@ $^ -lspdlog -lpthread -lfmt
.PHONY : clean
clean:
rm -f stdout


在这个日志等级中,我们对应的等级高于DEBUG的就会进行输出
cpp
/*
标准输出的日志:
1. 包含头文件 <spdlog/spdlog.h>
2. 创建日志对象
3. 设置日志输出等级
4. 设置日志输出格式
5. 输出日志
*/
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <iostream>
int main(int argc, char *argv[])
{
auto logger = spdlog::stdout_color_mt("stdout");
// logger->set_level(spdlog::level::debug);
logger->set_level(spdlog::level::info);
logger->set_pattern("[%H:%M:%S][%7l]: %v");
logger->debug("{} 今年 {}岁","小明",18);
logger->info("{} 今年 {}岁","小红",19);
logger->warn("{} 今年 {}岁","小刚",20);
logger->error("{} 今年 {}岁","小绿",21);
return 0;
}

这里我们将日志level设置成为了info,意思就是所有大于等于info的都进行输出
b.文件输出
cpp
/*
标准输出的日志:
1. 包含头文件 <spdlog/spdlog.h>
2. 创建日志对象
3. 设置日志输出等级
4. 设置日志输出格式
5. 输出日志
*/
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <iostream>
int main(int argc, char *argv[])
{
auto logger = spdlog::basic_logger_mt("file_logger","./file.dat");
logger->set_level(spdlog::level::info);
logger->set_pattern("[%H:%M:%S][%7l]: %v");
logger->debug("{} 今年 {}岁","小明",18);
logger->info("{} 今年 {}岁","小红",19);
logger->warn("{} 今年 {}岁","小刚",20);
logger->error("{} 今年 {}岁","小绿",21);
return 0;
}


c.循环输出
cpp
/*
标准输出的日志:
1. 包含头文件 <spdlog/spdlog.h>
2. 创建日志对象
3. 设置日志输出等级
4. 设置日志输出格式
5. 输出日志
*/
#include <spdlog/spdlog.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <iostream>
int main(int argc, char *argv[])
{
auto logger = spdlog::rotating_logger_mt("rotate_logger","./rotate.dat",1024,3);
logger->set_level(spdlog::level::info);
logger->set_pattern("[%H:%M:%S][%7l]: %v");
for(int i = 0;i < 10000;i++)
{
logger->info("hello world - {}",i);
}
return 0;
}


文件的内容会进行覆盖,所以会导致文件内容是是最新的内容(不会一直堆积在一个文件中)
后续还有写入数据库的操作,网络写入等
d.异步日志使用
cpp
/*
标准输出的日志:
1. 包含头文件 <spdlog/spdlog.h>
2. 创建日志对象
3. 设置日志输出等级
4. 设置日志输出格式
5. 输出日志
*/
#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <iostream>
int main(int argc, char *argv[])
{
auto logger = spdlog::stdout_color_mt<spdlog::async_factory>("stdout_logger");
logger->set_level(spdlog::level::info);
logger->set_pattern("[%H:%M:%S][%7l]: %v");
for(int i = 0;i < 10000;i++)
{
logger->info("hello world - {}",i);
}
return 0;
}
但是我们目前还是无法体现这个同步和异步的效率的差别
5.日志组件对比



6.封装
a.设计




b.实现






然后log.cc 和 log.h进行分离

log.h
cpp
/*
日志操作封装:
1.防止头文件重复包含
2.包含头文件
3.声明命名空间
4.声明全局日志器
5.声明日志配置结构体
6.声明全局日志器初始化接口
7.封装日志输出宏
*/
// 防止头文件重复包含
#pragma once
// 包含头文件
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/async.h>
#include <iostream>
// 声明命名空间
namespace logger
{
// 声明全局日志器
extern std::shared_ptr<spdlog::logger> g_logger;
// 声明日志配置结构体
struct LogConfig
{
bool async; // 是否异步
int level; // 日志等级: 1-debug 2-info 3-warn 4-error 6-off
std::string format; // 日志格式: [%H:%M:%S][%-7l]: %v
std::string path; // 日志目标: stdout 或 ./log.dat
};
// 声明全局日志器初始化接口
extern void log_init(const LogConfig& config);
// 声明日志输出宏
#define FMT_PREFIX std::string("[{}:{}]: ")
#define DBG(fmt, ...) logger::g_logger->debug(FMT_PREFIX + fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define INF(fmt, ...) logger::g_logger->info(FMT_PREFIX + fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define WAR(fmt, ...) logger::g_logger->warn(FMT_PREFIX + fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define ERR(fmt, ...) logger::g_logger->error(FMT_PREFIX + fmt, __FILE__, __LINE__, ##__VA_ARGS__)
};
cpp
#include "log.h"
namespace logger
{
// 声明全局日志器
std::shared_ptr<spdlog::logger> g_logger;
// 实现全局日志器初始化接口
void log_init(const LogConfig& config)
{
// 1.判断日志器类型 -- async/sync
// 2.判断输出目标 -- stdout 或 文件
// 3.创建日志器
if(config.async == true)
{
if(config.path == "stdout")
{
g_logger = spdlog::stdout_color_mt<spdlog::async_factory>("stdout_logger");
}
else
{
g_logger = spdlog::basic_logger_mt<spdlog::async_factory>("file_logger",config.path);
}
}
else
{
if(config.path == "stdout")
{
g_logger = spdlog::stdout_color_mt("stdout_logger");
}
else
{
g_logger = spdlog::basic_logger_mt("file_logger",config.path);
}
}
// 4.设置日志输出等级
g_logger->set_level(spdlog::level::level_enum(config.level));
// 5.设置日志格式
g_logger->set_pattern(config.format);
}
}
log_test.cc
cpp
#include <log.h>
#include <gflags/gflags.h>
// 1.通过gflags定义要捕获的参数
DEFINE_bool(log_async,true,"是否异步输出日志");
DEFINE_string(log_path,"stdout","日志输出路径");
DEFINE_int32(log_level,1,"日志输出等级: 1-debug;2-info;3-warn;4-error;6-off");
DEFINE_string(log_format,"[%H:%M:%S][%7l]%v","日志输出格式");
int main(int argc,char* argv[])
{
// 2.解析命令行参数
gflags::ParseCommandLineFlags(&argc,&argv,true);
// 3.初始化日志器
logger::LogConfig config =
{
.async = FLAGS_log_async,
.level = FLAGS_log_level,
.format = FLAGS_log_format,
.path = FLAGS_log_path,
};
logger::log_init(config);
// 4.输出日志
DBG("{}今年{}岁","小明",18);
INF("{}今年{}岁","小红",19);
WAR("{}今年{}岁","小刚",20);
ERR("{}今年{}岁","小绿",21);
return 0;
}
makefile
bash
CFLAGS=-I../../source/ -std=c++17
log_test:log_test.cc ../../source/log.cc
g++ $(CFLAGS) -o $@ $^ -lspdlog -lpthread -lfmt -lgflags



进行提交之后






这样就合并好了

七.Jsoncpp的介绍


1.基本结构




2.JsonCpp安装
a.安装

b.JsonCpp的介绍

c.Json的使用






主要注意地方:







cpp
#include <iostream>
#include <jsoncpp/json/json.h>
int main()
{
Json::Value val;
val["name"] = "ltw";
val["age"] = 18;
val["score"].append(77.5);
val["score"].append(88.5);
val["score"].append(99.5);
std::cout << val.toStyledString() << std::endl;
return 0;
}



cpp
#include <jsoncpp/json/json.h>
#include <iostream>
#include <memory>
#include <sstream>
int main()
{
Json::Value val;
val["name"] = "ltw";
val["age"] = 18;
val["score"].append(77.5);
val["score"].append(88.5);
val["score"].append(99.5);
// 1.实例化建造者对象
// 2.通过建造者对象构造序列化对象
// 3.通过序列化对象进行序列化
Json::StreamWriterBuilder builder;
std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
std::stringstream ss;
int ret = writer->write(val, &ss);
if(ret != 0)
{
std::cout << "write json failed" << std::endl;
return -1;
}
std::cout << ss.str() << std::endl;
return 0;
}

cpp
#include <jsoncpp/json/json.h>
#include <iostream>
#include <memory>
#include <sstream>
int main()
{
Json::Value val;
val["name"] = "ltw";
val["age"] = 18;
val["score"].append(77.5);
val["score"].append(88.5);
val["score"].append(99.5);
// 1.实例化建造者对象
// 2.通过建造者对象构造序列化对象
// 3.通过序列化对象进行序列化
Json::StreamWriterBuilder builder;
builder["commentStyle"] = "None";
builder["indentation"] = "";
std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
std::stringstream ss;
int ret = writer->write(val, &ss);
if(ret != 0)
{
std::cout << "write json failed" << std::endl;
return -1;
}
std::cout << ss.str() << std::endl;
return 0;
}


commentStyle也可以不进行设置
cpp
#include <jsoncpp/json/json.h>
#include <iostream>
int main()
{
std::string json_str = R"({
"name": "ltw",
"age": 18,
"score": [77.5, 88.5, 99.5]
})";
Json::CharReaderBuilder builder;
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
Json::Value val;
std::string errors;
bool ret = reader->parse(json_str.c_str(), json_str.c_str() + json_str.size(), &val, &errors);
if(!ret)
{
std::cout << "parse json failed: " << errors << std::endl;
return -1;
}
std::cout << val.toStyledString() << std::endl;
std::cout << "name: " << val["name"].asString() << std::endl;
std::cout << "age: " << val["age"].asInt() << std::endl;
if(!val["score"].isNull() && val["score"].isArray())
{
for(int i = 0; i < val["score"].size(); i++)
{
std::cout << "score[" << i << "]: " << val["score"][i].asDouble() << std::endl;
}
}
return 0;
}

3.Jsoncpp的二次封装


cpp
/*
util工具封装
1.json序列化和反序列化
*/
#pragma once
#include <jsoncpp/json/json.h>
#include <iostream>
#include <memory>
#include <sstream>
#include <optional>
#include "log.h"
namespace util
{
class JsonUtil
{
public:
// json序列化
static std::optional<std::string> serialize(const Json::Value& val);
// json反序列化
static std::optional<Json::Value> deserialize(const std::string& json_str);
};
}
cpp
#include "util.h"
namespace util
{
std::optional<std::string> JsonUtil::serialize(const Json::Value& val) {
Json::StreamWriterBuilder builder;
builder["indentation"] = "";
std::unique_ptr<Json::StreamWriter> swp(builder.newStreamWriter());
std::stringstream ss;
int ret = swp->write(val, &ss);
if (ret != 0) {
ERR("序列化失败!");
return std::optional<std::string>();
}
return ss.str();
}
std::optional<Json::Value> JsonUtil::deserialize(const std::string& input) {
Json::CharReaderBuilder builder;
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
Json::Value val;
std::string errs;
bool ret = reader->parse(input.c_str(), input.c_str() + input.size(), &val, &errs);
if (ret == false) {
ERR("{} 反序列化失败:{}", input, errs);
return std::optional<Json::Value>();
}
return val;
}
}
cpp
#include "../../source/util.h"
void serialize_test()
{
Json::Value val;
val["name"] = "ltw";
val["age"] = 18;
val["score"].append(77.5);
val["score"].append(88.5);
val["score"].append(99.5);
auto ret = util::JsonUtil::serialize(val);
if(!ret) {
return;
}
std::cout << *ret << std::endl;
}
void deserialize_test()
{
std::string json_str = R"({"name": "ltw", "age": 18, "score": [77.5, 88.5, 99.5]})";
auto val = util::JsonUtil::deserialize(json_str);
if (!val) {
return;
}
std::cout << (*val)["name"].asString() << std::endl;
std::cout << (*val)["age"].asInt() << std::endl;
if(!(*val)["score"].isNull() && (*val)["score"].isArray()) {
for(auto& score : (*val)["score"]) {
std::cout << score.asDouble() << std::endl;
}
}
}
int main()
{
serialize_test();
deserialize_test();
return 0;
}
cpp
json_test : json_test.cc ../../source/util.cc ../../source/log.cc
g++ -std=c++17 $^ -o $@ -ljsoncpp -lfmt -lspdlog -lpthread -lgflags
.PHONY:clean
clean:
rm -rf json_test

八.cpp-httplib的介绍
1.介绍

2.安装

3.使用接口





4.实现原理


5.使用
cpp
/*
使用HTTPLIB搭建HTTP服务器
1. 包含头文件
2. 实例化Server对象
3. 注册路由信息 -- 告诉httplib收到哪个请求应该由哪个接口进行处理
4. 启动服务器
*/
#include <httplib.h>
void HelloWorld(const httplib::Request& req, httplib::Response& res)
{
std::cout << req.method << std::endl;
std::cout << req.path << std::endl;
std::cout << req.body << std::endl;
for(auto it : req.headers)
{
std::cout << it.first << " : " << it.second << std::endl;
}
for(auto it : req.params)
{
std::cout << it.first << " : " << it.second << std::endl;
}
std::string html_body = "<html><body><h1>Hello World!</h1></body></html>";
res.set_content(html_body, "text/html");
res.status = 200;
}
int main()
{
httplib::Server svr;
svr.Get("/hello", HelloWorld);
svr.Get(R"(/number/(\d+))", [](const httplib::Request& req, httplib::Response& res)
{
std::cout << req.method << std::endl;
std::cout << req.path << std::endl;
std::string number = req.matches[1];
std::string html_body = "<html><body><h1>Number: " + number + "</h1></body></html>";
res.set_content(html_body, "text/html");
res.status = 200;
});
svr.listen("0.0.0.0", 8080);
return 0;
}

bash
server:server.cc
g++ -std=c++17 $^ -o $@ -lpthread
.PHONY:clean
clean:
rm -rf server
启动服务器:




