当前位置: 首页 > article >正文

【云备份】httplib库

目录

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;//请求资源路径——URL
        Headers headers;//头部字段
        std::string body;//正文部分
        std::string version;//协议版本
        Params params;//查询字符串
        MultipartFormDataMap files;//客户端上传文件的信息
        Ranges ranges;//实现断点续传的请求区间
        //提供的API
        //查询头部字段key是否存在
        bool has_header(const char *key) const;
        //获取头部字段的value
        std::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.1
    int 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,和Response
        using 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;
        //搭建并启动http
        bool 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表中设置映射关系

请求方法,请求资源和处理函数都是强相关的,如上表:

  1. 只有http请求的请求方法是GET,且请求的资源是/Hello/a,服务器才会调用echoHelloA处理请求
  2. 只要请求方法和请求资源在Handlers表中找不到映射的处理函数,http服务器就会返回404的http响应,表示请求资源不存在
  3. Get("/Hello/a", echoHelloA):将请求方法为GET,请求资源为/Hello/a,与函数echoHelloA注册到Handlers表中

线程池的工作:

  1. 当服务器接收到一个http请求,会将该http请求放入线程池,线程池的线程会调用相应的函数解析http请求,形成一个Request对象
  2. 在Handlers映射表中查找有无对应请求方法和请求资源的处理函数,有则调用处理函数,并构建http响应
  3. 处理函数调用完后,将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库搭建简单服务器

搭建服务器:

  1. 定义server对象
  2. 在Handlers表中注册相应请求方法和请求资源的处理函数
  3. 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请求:

  1. /hello:当客户端向服务器发送GET请求到/hello路由时,服务器将返回文本内容"Hello World!"。
  2. /number/(\d+):这是一个带有正则表达式的路由,用于匹配形如/number/123的URL,其中123可以是任意数字。当匹配成功时,服务器将返回该数字作为文本内容。

POST请求:

  • /upload:当客户端向服务器发送POST请求到/upload路由,并且请求中包含名为"file"的文件时,服务器将处理该文件上传。服务器将返回上传文件的文件名和文件内容作为文本响应。

需要注意的是,这个程序只能处理上述特定路由上的请求。如果客户端发送请求到未定义的路由,服务器将不会返回任何响应(或者根据httplib的默认行为,可能会返回一个404错误)。

 

7.httplib库搭建简单服务器

简单客户端

  1. 定义Clinet对象,绑定要连接的服务器的IP地址和端口号
  2. 构建请求
  3. 获取响应,处理响应
#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;
}


http://www.kler.cn/a/380836.html

相关文章:

  • 论文《Vertical Federated Learning: Concepts, Advances, and Challenges》阅读
  • C++ Learning string类的使用
  • Linux文件目录 --- 移动和改名命令MV、强制移动、试探性移动过、按时间移动
  • WebAPI编程(第一天,第二天)
  • 如何根据一系列提交文件,匹配对应的git提交记录?用ai
  • 单片机:实现自动关机电路(附带源码)
  • 信息安全工程师(77)常见网络安全应急事件场景与处理流程
  • 拓展学习-golang的基础语法和常用开发工具
  • 【LeetCode】【算法】234.回文链表
  • Spring Data Redis的基本使用
  • Spring Boot 与 Vue 共铸卓越采购管理新平台
  • OpenID Connect 和 OAuth 2.0 有什么不同?
  • 揭秘rust中默认参数类型不为人知的秘密,你确定不来了解下吗?
  • Java 基于SpringBoot+Vue 的公交智能化系统,附源码、文档
  • Django Form 实现多层(嵌套)模型表单
  • 深度学习模块创作(缝合)教程|适合1-360月小宝宝食用,干货满满
  • 深度学习基础知识-损失函数
  • 【C/C++】memcpy函数的模拟实现
  • Mac OS 配置Docker+Mysql
  • C++中的继承——第一篇
  • ​CSS之三
  • 【OJ题解】C++实现字符串大数相乘:无BigInteger库的字符串乘积解决方案
  • vue中强制更新视图
  • 网络信息系统的整个生命周期
  • 服务器作业2
  • AUTOSAR COM 模块的主要功能导读以及示例