文章目录
- 1. 下载 httplib 库
- 2. 从 Win 传输文件到 Linux
- 3. 解压缩
- httplib 库
- 1. struct Request 结构体源码展示
- 2. struct Reponse 结构体源码展示
- 3. httplib 库 Server 类
- 4. httplib 库 Client 类
- 5. 搭建简易 server 服务器
- 6. 搭建简易 client 客户端
1. 下载 httplib 库
-
要求联网,笔者使用云服务器,在 Xshell 7 上完成的虚拟机操作。
-
安装 git 工具,通过代码 clone
(大概率连接不上,推荐直接浏览器下载)
sudo yum install -y git
git clone https://github.com/yhirose/cpp-httplib.git
- 浏览器访问链接下载压缩包
🔗Github 链接
2. 从 Win 传输文件到 Linux
- 下载文件传输工具
sudo yum install -y lrzsz
- 笔者使用 Xshell7,可以将下载好的两个压缩包直接拖拽到 Xshell 界面后接收。也可以手下下面代码,在弹窗中选择两个压缩包文件进行传输。
rz -E
3. 解压缩
- 安装解压缩工具
sudo yum install -y unzip
- 对文件解压缩
unzip cpp-httplib-master.zip
httplib 库
httplib 库,一个 C++11 单文件头的跨平台 HTTP/HTTPS 库。安装起来非常容易。只需包含
httplib.h
在代码中即可。
httplib 库实际上是用于搭建一个简单的 http 服务器或者客户端的库,这种第三方网络库,可以让我们免去搭建服务器或客户端的时间,把更多的精力投入到具体的业务处理中,提高开发效率。
1. struct Request 结构体源码展示
Request 结构体的作用:
- 客户端保存的所有 http 请求相关信息,最终组织 http 请求发送给服务器
- 服务器收到 http 请求之后进行解析,将解析的数据保存在 Request 结构体中, 待后续处理
Request 源代码(部分):
struct Request {
std::string method; // 请求方法
std::string path; // 资源路径
Headers headers; // 头部字段
std::string body; // 正文
// for server
std::string version; // 协议版本
Params params; // 查询字符串
MultipartFormDataMap files; // 保存的是客户端上传的文件,里面有四个字段,详细代码见后文
Ranges ranges; // 用于实现断点续传的请求文件区间
// API
bool has_header(const std::string &key) const; // 查询 headers 里面有没有某个字段
std::string get_header_value(const std::string &key, size_t id = 0) const; // 获取头部字段
void set_header(const std::string &key, const std::string &val); // 设置头部字段的值
bool has_file(const std::string &key) const; // 是否包含某个文件,从 files 字段中获得的
MultipartFormData get_file_value(const std::string &key) const; // 获取文件信息
};
struct MultipartFormData {
std::string name; // 字段名称
std::string content; // 字段内容
std::string filename; // 文件名称
std::string content_type; // 正文类型
};
2. struct Reponse 结构体源码展示
Reponse 结构体的作用:
- 用户将响应数据放到结构体中,httplib 会将其中的数据按照 http 响应格式组织成为 http 响应,发送给客户端。
Request 源代码(部分):
struct Response {
std::string version; // 协议版本
int status = -1; // 响应版本
Headers headers; // 头部字段
std::string body; // 响应给客户端的正文
// API
void set_header(const std::string &key, const std::string &val); // 设置头部字段,放入 headers
void set_content(const std::string &s, const std::string &content_type); // 设置正文,放入 body
};
3. httplib 库 Server 类
httplib 库的 Server 类,就是用来快速搭建 http 服务器的。
class Server {
using Handler = std::function<void(const Request &, Response &)>; // 处理请求任务回调函数
using Handlers = std::vector<std::pair<std::regex, Handler>>; // 请求与处理函数映射表
std::function<TaskQueue *(void)> new_task_queue; // 线程池,处理 http 请求
// 针对某种请求方法的某个请求设定映射的处理函数
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);
// 搭建并启动 http 服务器
bool listen(const char *host, int port, int socket_flags = 0);
};
Handler
:函数指针类型,定义了一个 http 请求任务处理 回调函数 的格式。
using Handler = std::function<void(const Request &, Response &)>;
- 参数 Request:保存请求数据,让用户能够根据请求数据进行业务处理。
- 参数 Response:需要用户在业务处理中,填充数据,最终要响应给客户端。
httplib 搭建的服务器收到请求后,进行解析,得到一个 Request 结构体,其中根据请求数据 server 去处理这个请求,这个函数定义的格式就是 Handler 格式。
Handlers
:请求路由数组,其中包含了两个信息,
using Handlers = std::vector<std::pair<std::regex, Handler>>;
- 参数 regex:正则表达式,用于匹配 http 请求资源路径
- 参数 Handler:请求处理函数指针
可以理解作,Handlers是一 张表, 将一个客户端请求的资源路径 和一个处理函数(用户自己定义的函数) 建立映射关系。
当服务器收到请求解析得到 Request 就会根据资源路径以及请求方法到这张表中查看有没有对应的处理函数。如果有则调用这个函数进行请求处理,如果没有则响应 404。
简单说来,handlers 这个表就决定了,哪个请求应该用哪个函数处理。
new_ task_ queue
:线程池,处理 http 请求。httplib 收到一个新建连接,则将新的客户端连接抛入线程池中。
std::function<TaskQueue *(void)> new_task_queue;
线程池中线程的工作 :
- 接收请求,解析请求,得到 Request 结构体也就是请求的数据
- 在 Handlers 映射表中,根据请求信息查找处理函数,如果有则调用函数处理 void(const Request &, Response &)
- 当处理函数调用完毕,根据函数返回的 Response 结构体中的数据组织 http 响应发送给客户端
4. httplib 库 Client 类
httplib 库的 Client 类,是用来快速搭建 http 客户端的。
class Client {
Client(const std::string &host, int port); // 传入服务器 IP 和 端口
// 向客户端发送的各种请求
Result Get(const char *path, const Headers &headers);
Result Post(const char *path, const char *body, size_t content_length,const char *content_type);
Result Post(const char *path, const MultipartFormDataItems &items); // Post 请求提交多区域数据,常用于多文件上传,类型源码见上文
};
5. 搭建简易 server 服务器
搭建 server.cpp 代码如下:
#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,往后的下标中保存捕捉的数据即,.matches[]中
rsp.set_content(num, "text/plain");
rsp.status = 200;
}
void Multipart(const httplib::Request &req, httplib::Response &rsp)
{
auto ret = req.has_file("filexxx");
if (ret == false)
{
std::cout << "not file upload\n";
rsp.status = 404;
}
const auto& file = req.get_file_value("filexxx"); // 获取文件区域数据信息
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()
{
httplib::Server server; // 实例化一个 server 类的对象用于搭建服务器
server.Get("/hi", Hello); // 注册一个针对 /hi 的 Get 请求的处理函数映射关系
server.Get("/numbers/(\\d+)", Numbers);
// server.Get(R"(/numbers/(\d+))", Numbers); // 用 R() 表示圆括号里的都是原始字符
/*
正则表达式:
\,将下一个字符标记
(),用来捕捉信息
+,匹配加号前面子表达式一次或多次
*/
server.Post("/multipart", Multipart);
server.listen("0.0.0.0", 9090); // IP 这里代表匹配所有的网卡
return 0;
}
- bash 中编译代码:
g++ -o server server.cpp -lpthread -std=c++11
- 运行代码:
./server
-
借助浏览器,对我们的 server 进行访问 输入
[server 端公网 IP]\:[端口号]\/[请求]
如下:
注意:如果浏览器无法加载,是因为端口号被禁了,处理方式如下:
虚拟机 需要关闭防火墙:
sudo systemctl stop firewalld
sudo systemctl disable firewalld
云服务器 需要在官网中设置安全组策略,笔者使用阿里云,界面如下:
6. 搭建简易 client 客户端
搭建 client.cpp 代码如下:
#include "httplib.h"
#define SERVER_IP "你的server端公网IP" // 服务器的地址
#define SERVER_PORT 9090 // 服务器的端口号
int main()
{
httplib::Client clt(SERVER_IP, SERVER_PORT);
httplib::MultipartFormData item;
item.name = "filexxx";
item.filename = "hello.txt";
item.content = "hello world~~"; // 如果上传的是文件,这里就是文件内容
item.content_type = "text/plain";
httplib::MultipartFormDataItems items;
items.push_back(item);
auto res = clt.Post("/multipart", items);
std::cout << res->status << std::endl;
std::cout << res->body << std::endl;
return 0;
}
- bash 中编译代码:
g++ -o client client.cpp -lpthread -std=c++11
- 在两个会话中,运行代码:
./server
./client
- 相当于 client 发送了请求,server 收到并传回相应,结果如下:
🥰如果本文对你有些帮助,请给个赞或收藏,你的支持是对作者大大莫大的鼓励!!(✿◡‿◡) 欢迎评论留言~~