目录
1.httplib库简介
2.httplib请求类
3.httplib响应类
4.Server类
5.Client类
6.httplib库搭建简单服务器
6.1.ubuntu20.04使用防火墙开放端口
6.2.效果
7.httplib库搭建简单服务器
注意:如果对HTTP不熟悉就去:【网络】HTTP_yum install telnet-CSDN博客
在当今的软件开发中,与网络通信相关的任务变得日益普遍。HTTP(Hypertext Transfer Protocol)作为互联网通信的核心协议之一,扮演着连接客户端与服务器的桥梁。为了简化开发人员对HTTP的处理,httplib 库应运而生。这个C++库提供了简单且高效的方法来创建HTTP服务器和客户端,使得开发人员能够更加轻松地构建Web应用程序、微服务和网络连接的应用。本文将深入探讨httplib库的特性、用法以及如何在C++项目中集成它,帮助读者更好地理解和利用这个强大的工具。
1.httplib库简介
httplib 是一个轻量级的 C++ 库,用于创建 HTTP 服务器和客户端。它的设计简单而灵活,非常容易设置。只需在代码中包含 httplib.h 文件即可!使得开发者能够在自己的 C++ 项目中轻松地实现基本的 HTTP 通信功能。
特性 | 说明 |
---|---|
简单易用的接口 | httplib 提供了简单易用的接口,使得创建 HTTP 服务器和客户端变得非常容易。通过少量的代码,你就能够建立起一个功能完善的 HTTP 服务器或客户端。 |
支持主要 HTTP 方法 | httplib 支持主要的 HTTP 方法,包括 GET、POST、PUT、DELETE 等,让你能够处理各种类型的 HTTP 请求。 |
灵活的路由处理 | 使用 httplib,你可以轻松地定义路由和处理函数,根据不同的 URL 请求调用相应的处理函数。这使得构建 RESTful API 或者 Web 应用程序变得非常方便。 |
支持静态文件服务 | httplib 还提供了静态文件服务的功能,你可以将指定目录下的文件直接暴露给客户端访问,而无需额外处理。 |
简洁的代码库 | httplib 的代码库非常简洁,没有过多的依赖,易于集成到你的项目中。这使得它成为了许多开发者选择的首选 HTTP 库之一。 |
活跃的开发和社区支持 | httplib 的开发仍在持续进行中,它的代码库得到了广大开发者社区的支持和贡献,因此可以期待它会持续改进和更新。 |
总的来说,httplib 是一个功能强大且易于使用的 C++ HTTP 库,适用于各种类型的项目,无论是小型工具还是大型 Web 应用程序。通过它,你可以快速地搭建起自己的 HTTP 服务器或客户端,实现各种网络通信需求。
2.httplib请求类
还记得HTTP请求报头吗
httplib中封装了一个结构——Request,用来保存客户端/浏览器发送的HTTP请求的内容。当服务器获取到客户端的请求,httplib会将请求字符串解析成Request
struct Request {//成员变量std::string method;//请求方法std::string path;//请求资源路径——URLHeaders headers;//头部字段std::string body;//正文部分std::string version;//协议版本Params params;//查询字符串MultipartFormDataMap files;//客户端上传文件的信息Ranges ranges;//实现断点续传的请求区间//提供的API//查询头部字段key是否存在bool has_header(const char *key) const;//获取头部字段的valuestd::string get_header_value(const char *key, size_t id = 0) const;//设置头部字段void set_header(const char *key, const char *val);//查询客户端是否上传该文件bool has_file(const char *key) const;//获取文件信息MultipartFormData get_file_value(const char *key) const;};
其中有一个文件结构体和文件结构体数组
struct MultipartFormData {std::string name;//字段名称std::string content;//文件内容std::string filename;//文件名std::string content_type;//文件类型
};
//文件结构体数组
using MultipartFormDataItems = std::vector<MultipartFormData>;
3.httplib响应类
大家应该都记得http响应报头吧
httplib响应类是Responce,需要用户手动填补,httplib返回响应时,会将Responce组织成字符串返回给客户端
struct Response {std::string version; //协议版本号,默认时http1.1int status = -1; //响应状态码,std::string reason; Headers headers; //响应报头std::string body; //响应正文std::string location; // 重定向位置
//两个比较重要的API
//以key-val将相应的字段设定到响应报头中
void set_header(const char *key, const char *val);
//设置正文——可以设置类型
void set_content(const std::string &s, const char *content_type);
};
4.Server类
Server类就是httplib中用于搭建服务器的类
class Server {//Handler一个函数指针,它的返回值为void,参数是Request,和Responseusing Handler = std::function<void(const Request &, Response &)>;//Handlers是一个映射表,将请求资源和处理函数映射在一起using Handlers = std::vector<std::pair<std::regex, Handler>>;//将Get方法的请求资源与处理函数加载到Handlers表中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);//线程池std::function<TaskQueue *(void)> new_task_queue;//搭建并启动httpbool listen(const char *host, int port, int socket_flags = 0);};
首先,Handler是一个回调的函数指针。Handlers是一个映射表。std::regex是正则表达式,用于匹配请求资源,即URL中的path。Handlers将请求的资源和处理函数进行映射。当服务器收到一个请求,解析Request,获取客户端请求的资源路径,在Handlers表中找是否存在映射关系,有则调用对应的Handler回调函数处理请求,无则返回404(请求资源不存在)。
上面的Get,Post,Put,Patch,Delete,Options都是往Handlers表中设置映射关系
请求方法,请求资源和处理函数都是强相关的,如上表:
- 只有http请求的请求方法是GET,且请求的资源是/Hello/a,服务器才会调用echoHelloA处理请求
- 只要请求方法和请求资源在Handlers表中找不到映射的处理函数,http服务器就会返回404的http响应,表示请求资源不存在
- Get("/Hello/a", echoHelloA):将请求方法为GET,请求资源为/Hello/a,与函数echoHelloA注册到Handlers表中
线程池的工作:
- 当服务器接收到一个http请求,会将该http请求放入线程池,线程池的线程会调用相应的函数解析http请求,形成一个Request对象
- 在Handlers映射表中查找有无对应请求方法和请求资源的处理函数,有则调用处理函数,并构建http响应
- 处理函数调用完后,将Responce构建成http响应,并将构建好的http响应返回给客户端
5.Client类
class Client {//构造一个客户端对象,传入服务器Ip地址和端口Client(const std::string &host, int port);//向服务器发送GET请求Result Get(const char *path, const Headers &headers);//向服务器发送Post请求//path是路径,body是request请求路径//content_length是正文大小//content_type是正文的类型Result Post(const char *path, const char *body, size_t content_length,const char *content_type);//以Post方法上传文件Result Post(const char *path, const MultipartFormDataItems &items);
}
6.httplib库搭建简单服务器
搭建服务器:
- 定义server对象
- 在Handlers表中注册相应请求方法和请求资源的处理函数
- listen绑定IP地址和端口号,启动服务器,监听连接和请求
main.cc(函数版本)
#include <iostream>
#include "httplib.h"
using namespace httplib;
void Hello(const Request &req, Response &rsp)
{ rsp.set_content("Hello World!", "text/plain");rsp.status = 200; // 设置状态码
}
void Numbers(const Request &req, Response &rsp)
{ auto num = req.matches[1]; // 0里边保存的是整体path,往后下标中保存的是捕捉的数据rsp.set_content(num, "text/plain");rsp.status = 200;
}
void Multipart(const Request &req, Response &rsp)
{auto ret = req.has_file("file");if(ret == false){std::cout << "not file upload\n";rsp.status = 400;return;}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;return;
}
int main()
{httplib::Server sever; // 实例化Sever对象用于搭建服务器sever.Get("/hi", Hello); // 注册一个针对/hi的Get请求的处理函数映射关系sever.Get(R"(/numbers/(\d+))", Numbers);sever.Post("/multipart", Multipart);sever.listen("0.0.0.0", 9090);return 0;
}
main.cc(正则表达式版本)
#include <iostream> // 引入标准输入输出流库
#include "httplib.h" // 引入httplib库,用于创建HTTP服务器
#include <string> // 引入字符串库 int main()
{ httplib::Server server; // 创建一个httplib的Server对象,用于启动HTTP服务器 // 设置处理函数 // GET请求方法,/hello请求资源的映射函数 server.Get("/hello", [](const httplib::Request& req, httplib::Response& resp){ resp.set_content("Hello World!", "text/plain"); // 设置响应内容为"Hello World!",并指定内容类型为文本 resp.status = 200; // 设置HTTP响应状态码为200,表示请求成功 }); // GET请求方法,/number/数字请求资源的映射函数 // 使用原始字符串字面量来避免转义字符的问题,(\d+)用于捕获数字 server.Get(R"(/number/(\d+))", [](const httplib::Request& req, httplib::Response& resp){ // matches[0]是完整请求资源路径,matches[1]是第一个(也是唯一一个)捕获的参数,即数字 auto num = req.matches[1]; // 从请求匹配中获取捕获的数字 resp.set_content(num, "text/plain"); // 将捕获的数字作为响应内容,并指定内容类型为文本 resp.status = 200; // 设置HTTP响应状态码为200,表示请求成功 }); // POST请求方法,/upload请求资源的映射函数 server.Post("/upload", [](const httplib::Request& req, httplib::Response& resp){ auto ret = req.has_file("file"); // 检查请求中是否包含名为"file"的文件 if(ret == false) // 如果没有上传文件 { resp.set_content("没有上传file文件", "text/plain"); // 设置响应内容为提示信息,并指定内容类型为文本 resp.status = 404; // 设置HTTP响应状态码为404,表示请求的资源未找到(这里可以视为未找到上传的文件) return -1; // 返回-1,表示处理函数执行失败(但在这个例子中,返回值并没有被使用) } auto size = req.files.size(); // 获取上传文件的数量(虽然在这个例子中我们只关心一个文件) const auto& file = req.get_file_value("file"); // 获取上传的文件信息(MultipartFormData结构体) // 构建响应内容 resp.body.clear(); // 清空响应体 resp.body = file.filename; // 将文件名添加到响应体中 resp.body += "\n"; // 添加换行符 resp.body += file.content; // 将文件内容添加到响应体中 resp.set_header("Content-Type", "text/plain"); // 设置响应头,指定内容类型为文本 resp.status = 200; // 设置HTTP响应状态码为200,表示请求成功 }); // 启动服务器,绑定到所有可用的IP地址(0.0.0.0)和指定的端口号(8080) server.listen("0.0.0.0", 8080); // 这将阻塞当前线程,直到服务器被关闭 return 0; // 程序正常结束,返回0
}
以下是该程序的执行过程详解:
创建HTTP服务器:
- 在main函数内部,首先创建了一个httplib::Server对象。这个对象代表了一个HTTP服务器,它将负责监听和处理来自客户端的请求。
设置路由和处理函数:程序通过调用server.Get和server.Post方法为HTTP服务器设置了路由和处理函数。
- 对于/hello路由,当收到GET请求时,服务器将返回"Hello World!"作为响应。
- 对于/number/(\d+)路由,当收到GET请求并匹配到一个数字时,服务器将返回该数字作为响应。这里使用了正则表达式\d+来匹配一个或多个数字。
- 对于/upload路由,当收到POST请求并包含名为"file"的文件时,服务器将返回该文件的名字和内容作为响应。
启动服务器:
- 程序通过调用server.listen("0.0.0.0", 8080)来启动服务器。这里,"0.0.0.0"表示服务器将监听所有可用的网络接口,8080是服务器监听的端口号。
- listen函数将阻塞当前线程,等待客户端的连接和请求。这意味着在服务器运行期间,程序将停留在这一步,直到服务器被外部方式关闭(如通过发送终止信号或手动停止程序)。
处理客户端请求:
- 当有客户端向服务器发送请求时(例如,通过浏览器访问http://<服务器IP>:8080/hello),服务器将根据请求的URL和方法(GET、POST等)找到相应的处理函数。
- 处理函数将被调用,并接收两个参数:一个表示请求信息的httplib::Request对象和一个用于构建响应的httplib::Response对象。
- 处理函数根据请求的内容生成响应,并通过修改httplib::Response对象来设置响应的内容、状态码和头部信息。
- 最后,服务器将响应发送回客户端。
编译
首先我们要把那个httplib.h搞出来
然后我们通过下面这个编译即可
g++ -o server main.cc -std=c++14 -lpthread
接下来就能开始我们的测试了
6.1.ubuntu20.04使用防火墙开放端口
在Ubuntu 20.04中,默认使用的是ufw(Uncomplicated Firewall)作为防火墙管理工具。
使用ufw(默认防火墙管理工具)
打开防火墙
sudo ufw enable
关闭防火墙
sudo ufw disable
查看防火墙状态
sudo ufw status
开放TCP端口
sudo ufw allow 80/tcp # 开放TCP 80端口 sudo ufw allow 443/tcp # 开放TCP 443端口 sudo ufw allow 3306/tcp # 开放TCP 3306端口 sudo ufw allow 6379/tcp # 开放TCP 6379端口
关闭TCP端口
sudo ufw deny 80/tcp # 关闭TCP 80端口 sudo ufw deny 443/tcp # 关闭TCP 443端口 sudo ufw deny 3306/tcp # 关闭TCP 3306端口 sudo ufw deny 6379/tcp # 关闭TCP 6379端口
开放UDP端口
sudo ufw allow 9595/udp # 开放UDP 9595端口
关闭UDP端口
sudo ufw deny 9595/udp # 关闭UDP 9595端口
查看监听的TCP端口
sudo netstat -ntlp
查看监听的UDP端口
sudo netstat -nulp
查看所有开放的端口
sudo ufw status numbered
检测UDP的特定端口是否开放
# ufw没有直接的查询端口是否开放的命令,但你可以通过查看状态来确认
sudo ufw status numbered | grep 9595
检测TCP的特定端口是否开放
# 同上,通过查看状态来确认
sudo ufw status numbered | grep 80
对于ufw,通常命令执行后即时生效,不需要额外命令来使配置生效,ufw命令执行后即时生效
我们先打开防火墙,并且查看状态
接着我们开放8080端口
sudo ufw allow 8080/tcp
接着我们可以看看有没有开放成功
sudo ufw status numbered | grep 8080
开放成功了!!
注意:除了防火墙要开放端口外,还要在云服务器官网设置安全组
不懂的可以看这个:【网络】套接字(socket)编程——UDP版_udp 命名socket-CSDN博客
6.2.效果
我们可以来试一试效果
这个程序是一个基于httplib库的简单HTTP服务器,它能够处理特定路由上的GET和POST请求。以下是它能处理的客户端请求类型:
GET请求:
- /hello:当客户端向服务器发送GET请求到/hello路由时,服务器将返回文本内容"Hello World!"。
- /number/(\d+):这是一个带有正则表达式的路由,用于匹配形如/number/123的URL,其中123可以是任意数字。当匹配成功时,服务器将返回该数字作为文本内容。
POST请求:
- /upload:当客户端向服务器发送POST请求到/upload路由,并且请求中包含名为"file"的文件时,服务器将处理该文件上传。服务器将返回上传文件的文件名和文件内容作为文本响应。
需要注意的是,这个程序只能处理上述特定路由上的请求。如果客户端发送请求到未定义的路由,服务器将不会返回任何响应(或者根据httplib的默认行为,可能会返回一个404错误)。
7.httplib库搭建简单服务器
简单客户端
- 定义Clinet对象,绑定要连接的服务器的IP地址和端口号
- 构建请求
- 获取响应,处理响应
#include <iostream>
#include <string>
#include "httplib.h"int main()
{//连接服务器httplib::Client client("121.36.254.82", 8080);//构建文件结构体httplib::MultipartFormData item;item.name = "file";item.filename = "Hello.txt";item.content = "Hello World!";item.content_type = "text/plain";//文件结构体数组——传输多个文件httplib::MultipartFormDataItems items;items.push_back(item);//发送请求auto res = client.Post("/upload", items);//接收并处理响应std::cout << res->status << std::endl;std::cout << res->body << std::endl;
}