HTTP Client/Server 实践:cpp-httplib使用
目录
- 1.概述
- 2.快速使用cpp-httplib
- 1)安装
- 2)快速使用
- (1)创建simple_server.cpp
- (2)编译应用
- (3)运行和测试
- 3.使用CMake构建
- 1)构建步骤
- 2)自定义构建选项示例
- 3)CMake项目中使用cpp-httplib
- (1)查找包方法
- (2)find_package()后的可用变量
- 4)使用预编译头文件
- 5)高级配置
- (1)使用静态库依赖项
- (2)构建测试
- (3)将httplib.h拆分为.h和.cc文件
- 4.HTTP基本用法
- 1)HTTP服务器
- (1)最小HTTP服务器示例:
- (2)理解路由和处理器
- (3)处理请求和响应
- (4)常见操作
- (5)完整服务器示例
- 2)HTTP客户端
- (1)创建客户端
- (2)发起 HTTP 请求
- (3)添加请求头
- (4)处理响应数据
- (5)错误处理
- (6)高级客户端功能
- (7)完整示例
.
1.概述
cpp-httplib 是一个基于 C++11 的单头文件 HTTP/HTTPS 库,设计简洁,易于使用。只需包含一个头文件,即可快速实现 Web 服务器或 HTTP 客户端,支持现代 C++ 应用的服务器端与客户端通信。
主要功能
1)HTTP/HTTPS 服务器
-
简单路由 API:使用直观的语法定义端点处理程序
-
支持所有标准 HTTP 方法:GET、POST、PUT、DELETE、OPTIONS、PATCH
-
路径参数和正则匹配:直接从 URL 路径中提取值
-
静态文件服务:轻松从目录中提供文件
-
默认多线程:高效处理并发连接
-
SSL/TLS 支持:使用 HTTPS 保护服务器
2)HTTP/HTTPS 客户端
-
简单请求 API:用最少的代码发起 HTTP 请求
-
所有标准 HTTP 方法:完整的 HTTP 动词支持
-
认证:内置支持 Basic、Digest 和 Bearer token 认证
-
代理支持:通过代理服务器连接
-
重定向跟随:自动跟随 HTTP 重定向
-
内容处理:流式处理大请求/响应
.
.
2.快速使用cpp-httplib
1)安装
由于cpp-httplib是一个头文件库,安装只需将httplib.h
文件复制到您的项目中:
-
从仓库下载最新的
httplib.h
文件 -
将其包含到您的项目中
# 1.下载httplib.h:(https://github.com/yhirose/cpp-httplib)
wget https://github.com/yhirose/cpp-httplib/raw/refs/heads/master/httplib.h# 如果需要使用json,可以下载以下头文件,并引入# 下载nlohman_json.hpp:(https://github.com/nlohmann/json)wget https://github.com/nlohmann/json/releases/download/v3.12.0/json.hpp -O nlohman_json.hpp# 2.代码中引入httplib.h
#include "path/to/httplib.h"
.
2)快速使用
(1)创建simple_server.cpp
创建一个名为simple_server.cpp
的文件,内容如下:
#include <httplib.h>
#include <iostream>int main() {std::cout << "正在启动8080端口的服务器..." << std::endl;httplib::Server svr;svr.Get("/", [](const httplib::Request& /*req*/, httplib::Response& res) {res.set_content("<h1>欢迎使用cpp-httplib!</h1>", "text/html");});svr.Get("/json", [](const httplib::Request& /*req*/, httplib::Response& res) {res.set_content("{\"message\":\"Hello, JSON!\"}", "application/json");});svr.listen("0.0.0.0", 8080);
}
(2)编译应用
g++ -std=c++11 -o simple_server simple_server.cpp -pthread
注意:在许多系统上,使用-pthread
确保正确的多线程支持。
(3)运行和测试
- 运行您的服务器:
./simple_server
- 打开您的浏览器并访问:
-
http://localhost:8080/ - 您应该看到欢迎消息
-
http://localhost:8080/json - 您应该看到JSON响应
.
.
3.使用CMake构建
cpp-httplib主要设计为头文件库,只需在代码中包含httplib.h
即可轻松使用。然而,CMake集成提供了多项优势:
-
系统化的依赖管理(OpenSSL、ZLIB、Brotli、ZSTD)
-
可选编译为共享或静态库
-
正确安装到系统中
-
易于与其他基于CMake的项目集成
基本构建选项:
在构建之前,了解关键配置选项很有帮助:
选项 | 默认值 | 描述 |
---|---|---|
HTTPLIB_COMPILE | OFF | 作为编译库而不是头文件库进行构建 |
BUILD_SHARED_LIBS | OFF | 构建为共享库(仅当HTTPLIB_COMPILE 为ON时) |
HTTPLIB_USE_OPENSSL_IF_AVAILABLE | ON | 如果可用,使用OpenSSL |
HTTPLIB_REQUIRE_OPENSSL | OFF | 如果未找到OpenSSL,构建失败 |
HTTPLIB_USE_ZLIB_IF_AVAILABLE | ON | 如果可用,使用ZLIB |
HTTPLIB_REQUIRE_ZLIB | OFF | 如果未找到ZLIB,构建失败 |
HTTPLIB_USE_BROTLI_IF_AVAILABLE | ON | 如果可用,使用Brotli |
HTTPLIB_REQUIRE_BROTLI | OFF | 如果未找到Brotli,构建失败 |
HTTPLIB_USE_ZSTD_IF_AVAILABLE | ON | 如果可用,使用ZSTD |
HTTPLIB_REQUIRE_ZSTD | OFF | 如果未找到ZSTD,构建失败 |
HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN | ON | 启用从macOS Keychain加载证书 |
HTTPLIB_INSTALL | ON | 启用安装目标 |
HTTPLIB_TEST | OFF | 构建并运行测试 |
这些选项允许你根据需要自定义cpp-httplib,启用或要求特定的压缩算法和SSL支持。
.
1)构建步骤
Linux/macOS
mkdir -p build
cd build# 配置项目
cmake -DCMAKE_BUILD_TYPE=Release ..# 构建库
cmake --build .# 可选安装(可能需要sudo)
sudo cmake --build . --target install
Windows
# 创建构建目录
mkdir build
cd build# 配置项目
cmake ..# 构建库
cmake --build . --config Release# 可选以管理员身份安装
runas /user:Administrator "cmake --build . --config Release --target install"
.
2)自定义构建选项示例
要使用特定功能进行构建:
mkdir -p build
cd build# 以编译库形式构建并要求OpenSSL
cmake -DHTTPLIB_COMPILE=ON -DHTTPLIB_REQUIRE_OPENSSL=ON ..# 以共享库形式构建并支持ZLIB
cmake -DHTTPLIB_COMPILE=ON -DBUILD_SHARED_LIBS=ON -DHTTPLIB_REQUIRE_ZLIB=ON ..# 使用所有压缩库进行构建
cmake -DHTTPLIB_REQUIRE_OPENSSL=ON -DHTTPLIB_REQUIRE_ZLIB=ON -DHTTPLIB_REQUIRE_BROTLI=ON -DHTTPLIB_REQUIRE_ZSTD=ON ..cmake --build .
.
3)CMake项目中使用cpp-httplib
安装cpp-httplib后,你可以在自己的CMake项目中使用它:
(1)查找包方法
cmake_minimum_required(VERSION 3.14)
project(MyProject)# 查找httplib及其可选组件
find_package(httplib COMPONENTS OpenSSL ZLIB Brotli zstd REQUIRED)# 创建你的可执行文件
add_executable(my_app main.cpp)# 链接到httplib
target_link_libraries(my_app PRIVATE httplib::httplib)
find_package()
中列出的组件告诉CMake你需要的可选功能,但即使不是所有组件都可用,库仍然会被找到。
(2)find_package()后的可用变量
调用find_package(httplib)
后,以下变量可用:
变量 | 描述 |
---|---|
HTTPLIB_HEADER_PATH | httplib.h头文件的完整路径 |
HTTPLIB_IS_USING_OPENSSL | 布尔值,指示是否启用OpenSSL支持 |
HTTPLIB_IS_USING_ZLIB | 布尔值,指示是否启用ZLIB支持 |
HTTPLIB_IS_USING_BROTLI | 布尔值,指示是否启用Brotli支持 |
HTTPLIB_IS_USING_ZSTD | 布尔值,指示是否启用ZSTD支持 |
HTTPLIB_IS_COMPILED | 布尔值,指示库是编译的还是头文件库 |
HTTPLIB_INCLUDE_DIR | httplib头文件的根路径 |
HTTPLIB_LIBRARY | 如果已编译,库的完整路径 |
httplib_VERSION | 项目的版本字符串 |
你可以在CMake配置中使用这些变量,根据可用功能调整构建。
.
4)使用预编译头文件
如果你想要使用预编译头文件(CMake自v3.16起支持的功能):
find_package(httplib REQUIRED)
target_precompile_headers(your_target INTERFACE "${HTTPLIB_HEADER_PATH}")
这在大型项目中可以显著提高构建速度。
.
5)高级配置
(1)使用静态库依赖项
要使用OpenSSL或Brotli的静态版本:
cmake -DOPENSSL_USE_STATIC_LIBS=ON -DBROTLI_USE_STATIC_LIBS=ON ..
这些选项告诉CMake查找依赖项的静态版本而不是共享库。
(2)构建测试
要构建并运行测试:
mkdir -p build
cd build
cmake -DHTTPLIB_TEST=ON ..
cmake --build .
ctest
这将编译并运行cpp-httplib的测试套件。
(3)将httplib.h拆分为.h和.cc文件
如果你更喜欢使用单独的头文件和实现文件,而不是单个头文件,cpp-httplib提供了一个Python脚本来拆分文件:
# 从仓库根目录
./split.py
# 这将创建out/httplib.h和out/httplib.cc
这在大型项目中通过将声明与实现分离,有助于减少编译时间。
在使用cpp-httplib的CMake构建系统时,请记住默认为头文件模式。
如果想将其作为编译库使用,必须设置-DHTTPLIB_COMPILE=ON
。
.
.
4.HTTP基本用法
1)HTTP服务器
可视化一个典型的cpp-httplib服务器的工作原理:
.
(1)最小HTTP服务器示例:
#include <httplib.h>int main() {// 创建服务器实例httplib::Server svr;// 定义GET /hi的路由处理器svr.Get("/hi", [](const httplib::Request& req, httplib::Response& res) {res.set_content("Hello World!", "text/plain");});// 启动服务器svr.listen("0.0.0.0", 8080);
}
这个简单的示例:
-
创建一个服务器实例
-
定义一个处理GET请求到"/hi"的路由处理器,返回纯文本响应
-
在所有接口(0.0.0.0)的8080端口上启动服务器
.
(2)理解路由和处理器
a.什么是路由(Routing)?
-
路由就是:当用户访问某个网址(URL)时,服务器决定“该调用哪个函数来处理这个请求”。
-
换句话说,路由 = URL 路径 + 对应的处理函数。
路由定义了你的API或Web服务器的端点。每个路由都与一个处理函数相关联,该函数处理请求并生成响应。
b.路由处理器基本结构
svr.METHOD("/path", [](const httplib::Request& req, httplib::Response& res) { // 你的处理器代码});
其中:
-
METHOD
是以下之一:Get, Post, Put, Delete, Options等 -
/path
是要匹配的URL路径 -
lambda函数接收请求和响应对象
c.路由中的模式匹配
cpp-httplib支持多种定义路由的方式:
- 简单静态路由
svr.Get("/hi", [](const httplib::Request& req, httplib::Response& res) {res.set_content("Hello World!", "text/plain");
});
- 正则表达式模式
// 这将匹配类似"/numbers/42"的路径,并将"42"在matches向量中提供。svr.Get(R"(/numbers/(\d+))", [&](const httplib::Request& req, httplib::Response& res) {auto numbers = req.matches[1]; // 捕获的值res.set_content(numbers, "text/plain");
});
- 命名路径参数
// 这将匹配类似"/users/123"的路径,并将"123"作为req.path_params["id"]提供。svr.Get("/users/:id", [&](const httplib::Request& req, httplib::Response& res) {auto user_id = req.path_params.at("id");res.set_content(user_id, "text/plain");
});
.
(3)处理请求和响应
请求对象(Request),提供了对以下内容的访问:
-
HTTP方法(
req.method
) -
URL路径(
req.path
) -
查询参数(
req.params
) -
头部(
req.headers
) -
正文(
req.body
) -
表单数据(
req.form
)
**响应对象(Response)**对象允许你:
-
设置响应状态(
res.status
) -
设置头部(
res.set_header()
) -
设置内容(
res.set_content()
) -
实现重定向(
res.set_redirect()
)
常见响应类型:
// HTML响应:
res.set_content("<h1>Hello World!</h1>", "text/html");// JSON响应:
res.set_content("{\"message\":\"Hello World!\"}", "application/json");// 重定向:
res.set_redirect("/new-location");
.
(4)常见操作
a.提供静态文件:
- cpp-httplib可以轻松地从目录中提供文件:
// 将/public路由挂载到./assets目录
auto ret = svr.set_mount_point("/public", "./assets");
if (!ret) {// 指定的目录不存在
}
- 你可以将多个目录挂载到同一路由。服务器将按顺序搜索它们:
// 首先在./primary中查找,然后在./fallback中查找
svr.set_mount_point("/public", "./primary");
svr.set_mount_point("/public", "./fallback");
b.错误处理:
- 为所有错误响应实现自定义错误处理:
svr.set_error_handler([](const httplib::Request& /*req*/, httplib::Response& res) {const char* fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";char buf[BUFSIZ];snprintf(buf, sizeof(buf), fmt, res.status);res.set_content(buf, "text/html");
});
c.请求日志:
- 添加日志以跟踪请求和响应:
svr.set_logger([](const httplib::Request& req, const httplib::Response& res) {printf("%s %s - %d\n", req.method.c_str(), req.path.c_str(), res.status);
});
d.连接设置
// 设置保持连接设置
svr.set_keep_alive_max_count(100); // 每个连接的最大保持连接请求数
svr.set_keep_alive_timeout(5); // 超时时间(秒)// 设置读写超时
svr.set_read_timeout(5, 0); // 5秒
svr.set_write_timeout(5, 0); // 5秒// 设置最大有效负载大小
svr.set_payload_max_length(1024 * 1024 * 50); // 50MB
e.控制服务器
- 启动服务器:
svr.listen("localhost", 8080);
- 停止服务器(从另一个线程或处理程序中):
svr.stop();
.
(5)完整服务器示例
下面是一个结合了各种功能的更全面的示例
#include <httplib.h>
#include <iostream>int main() {// 创建服务器实例httplib::Server svr;// 路由处理器svr.Get("/", [](const httplib::Request& /*req*/, httplib::Response& res) {res.set_redirect("/hello");});svr.Get("/hello", [](const httplib::Request& /*req*/, httplib::Response& res) {res.set_content("<h1>Hello, cpp-httplib!</h1>", "text/html");});// 带路径参数的路由svr.Get("/users/:id", [](const httplib::Request& req, httplib::Response& res) {auto user_id = req.path_params.at("id");res.set_content("User ID: " + user_id, "text/plain");});// POST处理器svr.Post("/submit", [](const httplib::Request& req, httplib::Response& res) {if (req.has_param("name")) {auto name = req.get_param_value("name");res.set_content("Hello, " + name + "!", "text/plain");} else {res.status = 400;res.set_content("Missing 'name' parameter", "text/plain");}});// 提供静态文件if (!svr.set_mount_point("/assets", "./public")) {std::cerr << "Failed to set mount point: directory doesn't exist\n";return 1;}// 自定义错误处理器svr.set_error_handler([](const httplib::Request& /*req*/, httplib::Response& res) {res.set_content("<h1>Error " + std::to_string(res.status) + "</h1>", "text/html");});// 日志记录器svr.set_logger([](const httplib::Request& req, const httplib::Response& res) {std::cout << req.method << " " << req.path << " - " << res.status << std::endl;});// 启动服务器std::cout << "Server starting on http://localhost:8080" << std::endl;svr.listen("localhost", 8080);return 0;
}
.
.
2)HTTP客户端
(1)创建客户端
要发起 HTTP 请求,需先创建一个客户端对象:
// 用于 HTTP 连接
httplib::Client cli("example.com", 80); // 主机和端口// 用于 HTTPS 连接(需在启用 SSL 支持的情况下构建)
httplib::SSLClient ssl_cli("example.com", 443);
也可用 scheme-host-port 格式创建客户端:
// 根据 scheme 自动选择 HTTP 或 HTTPS
httplib::Client cli("http://example.com:8080");
.
(2)发起 HTTP 请求
GET 请求:
- GET 请求默认会自动跳转到重定向目标地址,并自动保存和携带服务器设置的 Cookie(一小段数据)。
auto res = cli.Get("/path");
if (res) {// 请求成功 - 使用 resstd::cout << res->status << std::endl; // 状态码(例如 200)std::cout << res->body << std::endl; // 响应体
} else {// 请求失败 - 检查错误std::cout << "Error: " << res.error() << std::endl;
}
POST 请求
// 使用字符串数据POST
auto res = cli.Post("/resource", "Hello World", "text/plain");// 使用表单数据POST
httplib::Params params;
params.emplace("key1", "value1");
params.emplace("key2", "value2");
auto res = cli.Post("/form-endpoint", params);// 使用多部分表单数据POST(用于文件上传)
httplib::MultipartFormDataItems items = {{"text1", "plain text", "", ""},{"file1", "file content", "file.txt", "text/plain"},
};
auto res = cli.Post("/upload", items);
其他 HTTP 方法
// PUT请求
auto res = cli.Put("/resource", "Updated content", "text/plain");// DELETE请求
auto res = cli.Delete("/resource");// HEAD请求(仅获取头部,无正文)
auto res = cli.Head("/resource");// OPTIONS请求
auto res = cli.Options("*");// PATCH请求
auto res = cli.Patch("/resource", "Patch data", "text/plain");
.
(3)添加请求头
// 方法1:使用headers参数
httplib::Headers headers = {{"Authorization", "Bearer token12345"},{"User-Agent", "cpp-httplib/0.1"}
};
auto res = cli.Get("/api/resource", headers);// 方法2:为所有请求设置默认头部
cli.set_default_headers({{"Authorization", "Bearer token12345"},{"User-Agent", "cpp-httplib/0.1"}
});
auto res = cli.Get("/api/resource"); // 使用默认头部
.
(4)处理响应数据
当请求成功时,您可以操作响应对象:
auto res = cli.Get("/api/data");
if (res) {// 状态码int status = res->status;// 获取特定头部std::string content_type = res->get_header_value("Content-Type");// 响应体(字符串形式)std::string body = res->body;// 检查响应是否包含特定头部if (res->has_header("X-Custom-Header")) {// ...}// 遍历所有头部for (const auto& header : res->headers) {std::cout << header.first << ": " << header.second << std::endl;}
}
.
(5)错误处理
库使用类似结果的模式进行错误处理:
auto res = cli.Get("/api/data");
if (res) {// 请求成功// 使用 res->status、res->body 等
} else {// 请求失败auto err = res.error();switch (err) {case httplib::Error::Connection:std::cout << "Connection failed" << std::endl;break;case httplib::Error::TimeOut:std::cout << "Request timed out" << std::endl;break;// 处理其他错误情况...default:std::cout << "Error: " << httplib::to_string(err) << std::endl;break;}// SSL 验证错误(如果使用 SSLClient)if (res.error() == httplib::Error::SSLVerification) {auto result = cli.get_openssl_verify_result();if (result) {std::cout << "SSL verification error: " << X509_verify_cert_error_string(result) << std::endl;}}
}
.
(6)高级客户端功能
设置超时
// 设置连接超时
cli.set_connection_timeout(5, 0); // 5 秒,0 微秒
// 设置读取超时
cli.set_read_timeout(30, 0); // 30 秒,0 微秒
// 设置写入超时
cli.set_write_timeout(5, 0); // 5 秒,0 微秒// 使用 chrono(C++11 及以后)
cli.set_connection_timeout(std::chrono::seconds(5));
cli.set_read_timeout(std::chrono::seconds(30));
cli.set_write_timeout(std::chrono::seconds(5));
处理重定向
// 禁用自动跟随重定向(默认启用)
cli.follow_location(false);
// 设置最大重定向次数
cli.set_follow_location(true);
cli.set_follow_location_max_count(5); // 最多跟随 5 次重定向
SSL/TLS 配置:对于HTTPS连接,可配置SSL选项
httplib::SSLClient cli("example.com");
// 设置 CA 证书用于服务器验证
cli.set_ca_cert_path("./ca-bundle.crt");
// 启用服务器证书验证
cli.enable_server_certificate_verification(true);
// 客户端证书(用于双向 TLS)
cli.set_client_cert_path("client.crt", "client.key");
压缩
// 启用压缩(需在启用 ZLIB 支持的情况下构建)
cli.set_compress(true);
// 或者仅解压响应
cli.set_decompress(true);
保持连接:重用连接以提高性能
// 启用保持连接
cli.set_keep_alive(true);
// 禁用保持连接
cli.set_keep_alive(false);
一次性请求(静态方法):对于简单的一次性请求,无需创建客户端实例
// 静态 GET
auto res = httplib::Client("http://example.com").Get("/");
// 静态 POST
auto res = httplib::Client("http://example.com").Post("/api", "data", "text/plain");
.
(7)完整示例
以下是一个完整的示例,展示如何发起 GET 请求并处理响应:
#include <httplib.h>
#include <iostream>int main() {// 创建HTTP客户端httplib::Client cli("http://example.com");// 设置一些选项cli.set_connection_timeout(5, 0); // 5秒超时cli.set_follow_location(true); // 跟随重定向// 添加自定义头部httplib::Headers headers = {{"User-Agent", "MyClient/1.0"}};// 发出GET请求auto res = cli.Get("/api/data", headers);// 处理响应if (res) {// 成功std::cout << "状态: " << res->status << std::endl;std::cout << "内容类型: " << res->get_header_value("Content-Type") << std::endl;std::cout << "正文: " << res->body << std::endl;return 0;} else {// 错误std::cout << "HTTP错误: " << httplib::to_string(res.error()) << std::endl;return 1;}
}
以上内容涵盖了使用 cpp-httplib 作为客户端的基本要点。借助这些工具,您可以在 C++ 应用程序中向任意 Web 服务或 API 发起 HTTP 请求并处理响应。
.
声明:资源可能存在第三方来源,若有侵权请联系删除!