cpp-httplib 避免阻塞主线程, c++封装httplib,httplib面向对象开发
目录
- 说明/前言
- 原生的httplib会阻塞你的主线程
- 解决httplib阻塞主线程的问题
- BashController - 面向对象风格使用httplib
- 自定义controller --- TestController.h文件
- 使用controller功能的main.cpp文件
- get请求测试结果
- post请求测试结果
- HttpServer.h
说明/前言
~~~~~~~~ - cpp-httplib是一个简单易用, 跨平台, 开发快速, 仅头文件的c++ http库, 只需要把一个头文件包含到你的代码里, 就可以用很少的代码完成一个http server, 但它仍有一些不足之处, 比如会阻塞你的主线程(cpp-httplib的github地址)
~~~~~~~~ - 在不影响cpp-httplib原生代码的基础上, 对其进行了进一步的封装, 且完全和httplib解耦
~~~~~~~~ - 封装类为header-only, 直接inlude使用(封装的具体代码在文章最后的HttpServer.h中)
~~~~~~~~ - 避免了httplib原生使用方式阻塞主线程的问题
~~~~~~~~ - 在支持原生开发方式的基础上, 增加了BashController类,模仿java的spring mvc的开发方式, 以解耦代码, 支持较大型的web项目
~~~~~~~~ - 正文主要讲述使用方式, 封装的具体代码在文章最后, 可以直接复制使用
~~~~~~~~ - 跨平台, 可在linux和window中使用, 但原生的cpp-httplib对mingw支持不太好, windows下尽量使用msvc编译
~~~~~~~~ - 关于httplib更高级的用法, 请直接到(cpp-httplib的github地址)参考
~~~~~~~~ - 确保你的编译器最低支持c++14
原生的httplib会阻塞你的主线程
~~~~~~~~ cpp-httplib是一个简单易用, 跨平台, 开发快速, 仅头文件的c++ http库. 只需要把一个头文件包含到你的代码里, 就可以用很少的代码完成一个http server, 就像下面这样. 但是, 它会阻塞当前线程, 导致while循环中的cout不执行, 看下面的代码
#include
#include using namespace httplib;using namespace std;int main()
{httplib::Server svr;svr.Get("/hi", [](const httplib::Request &, httplib::Response &res) {res.set_content("Hello World!", "text/plain");});cout << "start http server" << endl; // listen函数后, 将阻塞当前线程svr.listen("0.0.0.0", 8080);// 由于主线程被阻塞, 因此下面的代码不会执行 while (true){cout << "main thread running ..." << endl;this_thread::sleep_for(chrono::seconds(2)); // 睡眠2秒}return 0;
}
执行结果

~~~~~~~~ 阻塞的原因是因为, 在httplib的listen函数中有一个while循环, 其中不停的执行accept()函数, 用来监听端口对应的文件描述符, 所以, 只要你开一个子线程, 让listen函数在子线程中执行, 就不会阻塞主线程了
~~~~~~~~ 但其实你不用太关心原因, 甚至不用操心线程, 因为这些我都给你封装好了 _
解决httplib阻塞主线程的问题
~~~~~~~~ 把HttpServer.h复制到httplib的同级目录下, 在你的程序中include它, 通过HttpServer.h使用httplib, 就不会阻塞你的主线程了 , 如下:(HttpServer.h代码较多, 放到文章最后了)
~~~~~~~~ HttpServer.h的代码在文章的最后, 你可以直接复制使用
#include
#include
#include // HttpServer.h头文件管理httplib::Server对象using namespace httplib;
using namespace std;int main()
{// 实例化一个HttpServer对象hs, 内部会自动初始化httplib::ServerHttpServer* hs = new HttpServer("0.0.0.0", 8080);// 获取hs内部的httplib::Server指针Server* svr = hs->getHttplibServer();// 使用httplib写服务器svr->Get("/hi", [](const httplib::Request &, httplib::Response &res) {res.set_content("Hello World!", "text/plain");});cout << "start http server" << endl; // HttpServer对象调用listenInThread(), 把listen函数放入一个子线程运行// 注意, 子线程运行后不能让main函数退出, 因为主线程被关闭后, 所有的子线程会被强制关闭hs->listenInThread();while (true){cout << "main thread running ..." << endl;this_thread::sleep_for(chrono::seconds(2));}return 0;
}
执行结果

BashController - 面向对象风格使用httplib
~~~~~~~ - HttpServer.h不但能自动为你的httplib::Server对象创建子线程, 还提供了一种类似于java spring mvc风格的http服务器开发方式, 以解耦你的代码. 让你的程序更加清晰明了.
~~~~~~~ - 你只需要继承BashController, 并按下面的方式编写的你服务器响应代码.
自定义controller — TestController.h文件
自定义controler, 解耦代码
#pragma once#include "Httpserver.h"class TestController : public httplib::BaseController
{// 注意: 必须重写bind()函数, 并在内部填写映射关系, 否则会抛出异常void bind() override{// BindController可以绑定httplib中所有参数为(const httplib::Request& req, httplib::Response& resp)的方法server->Get("/get", BindController(&TestController::getApi, this)); server->Post("/post", BindController(&TestController::postApi, this));}// 注意:Request和Response这两个参数必须是引用类型void getApi(const httplib::Request& req, httplib::Response& resp){std::string jsonData = "{\"type\": \"get\", \"code\": 2001}";resp.set_content(jsonData, "application/json");}// 注意:Request和Response这两个参数必须是引用类型void postApi(const httplib::Request& req, httplib::Response& resp){std::string data = req.body;std::cout << "接收请求数据: \n" << data << std::endl;std::string jsonData = "{\"type\": \"post\", \"code\": 2001}";resp.set_content(jsonData, "application/json");}
};
使用controller功能的main.cpp文件
当你把自定义的controllor写到头文件中, 只在main中include它们时, 你的main函数会非常简洁和清晰, 像下面这样:
#include
#include using namespace httplib;
using namespace std;// #include // include你自定义的controllor
#include // include你自定义的controllorint main()
{HttpServer* hs = new HttpServer("0.0.0.0", 8080);// MyController() >> hs; // 初始化MyController对象, 并注入到hsTestController() >> hs; // 初始化TestController对象, 并注入到hshs->listenInThread(); // 子线程中启动http serverwhile (true){cout << "main thread running ..." << endl;this_thread::sleep_for(chrono::seconds(2));}
}
get请求测试结果

post请求测试结果

HttpServer.h
~~~~~~~~ 具体的封装代码, 把它放在httplib.h的同级目录下, 就可以按文章中提到的方式使用他们了
// 将HttpServer.h放到httplib.h文件的同级目录下#pragma once
#include
#include
#include
#include
#include
#include
#include namespace httplib
{// 线程池(复制的httplib的线程池, 添加了动态增加线程数量的功能)class HttpServerThreadPool : public TaskQueue{public:explicit HttpServerThreadPool(size_t n) : shutdown_(false){while (n){threads_.emplace_back(worker(*this));n--;}}void addAThread(){threads_.emplace_back(worker(*this));}std::size_t getThreadNum(){return threads_.size();}HttpServerThreadPool(const ThreadPool &) = delete;~HttpServerThreadPool() override = default;void enqueue(std::function<void()> fn) override{{std::unique_lock<std::mutex> lock(mutex_);jobs_.push_back(std::move(fn));}cond_.notify_one();}void shutdown() override{// Stop all worker threads...{std::unique_lock<std::mutex> lock(mutex_);shutdown_ = true;}cond_.notify_all();// Join...for (auto &t : threads_){t.join();}}private:struct worker{explicit worker(HttpServerThreadPool &pool) : pool_(pool) {}void operator()(){for (;;){std::function<void()> fn;{std::unique_lock<std::mutex> lock(pool_.mutex_);pool_.cond_.wait(lock, [&]{ return !pool_.jobs_.empty() || pool_.shutdown_; });if (pool_.shutdown_ && pool_.jobs_.empty()){break;}fn = std::move(pool_.jobs_.front());pool_.jobs_.pop_front();}assert(true == static_cast<bool>(fn));fn();}}HttpServerThreadPool &pool_;};friend struct worker;std::vector<std::thread> threads_;std::list<std::function<void()>> jobs_;bool shutdown_;std::condition_variable cond_;std::mutex mutex_;};class BaseController;// HttpServer, 用于管理httplib::Server对象class HttpServer{private:std::string host = "";int port = -1;static httplib::HttpServerThreadPool *serverPool;httplib::Server *server = new httplib::Server();static std::atomic<int> poolUseNum;int socket_flags;void buildServerThreadPool();friend class BaseController;public:HttpServer(const HttpServer &) = delete;HttpServer() = delete;HttpServer(const std::string &_host, int _port, int _socket_flags = 0) : host(std::move(_host)), port(_port), socket_flags(_socket_flags) {}HttpServer(int _port, int _socket_flags = 0) : host(std::move("0.0.0.0")), port(_port), socket_flags(_socket_flags) {}httplib::Server* getHttplibServer();/*如果serverPool为null, 将为static serverPool 创建一个新的HttpThreadPool, 并在该线程池中监听如果serverPool不为null, 将直接使用static serverPool, 在线程池中执行httplib::Server的listen函数*/auto listenInThread();// 在本地监听httplib::Server的listen函数void listenInLocal();// 释放server指针,如果poolUseNum为0, 也将释放serverPool~HttpServer();};// BashController, 模仿java spring mvc的开发风格class BaseController{protected:// 必须重写bind方法, 在其中绑定具体的请求响应地址和请求响应方法, 否则抛出一个string异常virtual void bind(){throw std::string("must override ", __FUNCTION__);}httplib::Server *server = nullptr;// 绑定函数template<class Func, class T>auto BindController(Func&& func, T&& obj){httplib::Server::Handler handler = std::bind(func, obj, std::placeholders::_1, std::placeholders::_2);return handler;}public:BaseController() = default;BaseController(BaseController &) = delete;void operator>>(httplib::Server *_server){server = _server;// print("{} init to server", typeid(*this).name());this->bind();}void initToServer(httplib::Server *_server){server = _server;// print("{} init to server", typeid(*this).name());this->bind();}void operator>>(httplib::HttpServer *_server){server = _server->getHttplibServer();// print("{} init to server", typeid(*this).name());this->bind();}void initToServer(httplib::HttpServer *_server){server = _server->getHttplibServer();// print("{} init to server", typeid(*this).name());this->bind();}~BaseController(){if (server != nullptr){server = nullptr;}// print("destroy controller");}};}httplib::HttpServerThreadPool *httplib::HttpServer::serverPool = nullptr;
std::atomic_int httplib::HttpServer::poolUseNum(0);void httplib::HttpServer::buildServerThreadPool()
{poolUseNum++;if (serverPool == nullptr){serverPool = new httplib::HttpServerThreadPool{(std::size_t)poolUseNum.load()};}}httplib::Server *
httplib::HttpServer::getHttplibServer()
{return server;
}auto httplib::HttpServer::listenInThread()
{buildServerThreadPool();if (serverPool != nullptr){serverPool->addAThread();}std::cout << "listen to " << port << std::endl;return serverPool->enqueue(std::bind(&httplib::Server::listen, server, host, port, socket_flags));
}void httplib::HttpServer::listenInLocal()
{server->listen(host, port, socket_flags);// print("listen in port {}", port);
}httplib::HttpServer::~HttpServer()
{if (this->server != nullptr){delete (server);}poolUseNum--;if (poolUseNum == 0 && serverPool != nullptr){delete (serverPool);serverPool = nullptr;}
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
