要求联网,笔者使用云服务器,在 Xshell 7 上完成的虚拟机操作。
安装 git 工具,通过代码 clone
(大概率连接不上,推荐直接浏览器下载)
sudo yum install -y git
git clone https://github.com/yhirose/cpp-httplib.git
Github 链接
sudo yum install -y lrzsz
rz -E
sudo yum install -y unzip
unzip cpp-httplib-master.zip
httplib 库,一个 C++11 单文件头的跨平台 HTTP/HTTPS 库。安装起来非常容易。只需包含
httplib.h
在代码中即可。
httplib 库实际上是用于搭建一个简单的 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; // 正文类型
};
Reponse 结构体的作用:
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
};
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 &)>;
httplib 搭建的服务器收到请求后,进行解析,得到一个 Request 结构体,其中根据请求数据 server 去处理这个请求,这个函数定义的格式就是 Handler 格式。
Handlers
:请求路由数组,其中包含了两个信息,using Handlers = std::vector<std::pair<std::regex, Handler>>;
可以理解作,Handlers是一 张表, 将一个客户端请求的资源路径 和一个处理函数(用户自己定义的函数) 建立映射关系。
当服务器收到请求解析得到 Request 就会根据资源路径以及请求方法到这张表中查看有没有对应的处理函数。如果有则调用这个函数进行请求处理,如果没有则响应 404。
简单说来,handlers 这个表就决定了,哪个请求应该用哪个函数处理。
new_ task_ queue
:线程池,处理 http 请求。httplib 收到一个新建连接,则将新的客户端连接抛入线程池中。std::function<TaskQueue *(void)> new_task_queue;
线程池中线程的工作 :
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 请求提交多区域数据,常用于多文件上传,类型源码见上文
};
搭建 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;
}
g++ -o server server.cpp -lpthread -std=c++11
./server
借助浏览器,对我们的 server 进行访问 输入
[server 端公网 IP]\:[端口号]\/[请求]
注意:如果浏览器无法加载,是因为端口号被禁了,处理方式如下:
虚拟机 需要关闭防火墙:
sudo systemctl stop firewalld
sudo systemctl disable firewalld
云服务器 需要在官网中设置安全组策略,笔者使用阿里云,界面如下:
搭建 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;
}
g++ -o client client.cpp -lpthread -std=c++11
./server
./client
如果本文对你有些帮助,请给个赞或收藏,你的支持是对作者大大莫大的鼓励!!(✿◡‿◡) 欢迎评论留言~~