1、这是一个开源库github:GitHub - yhirose/cpp-httplib: A C++ header-only HTTP/HTTPS server and client library
2、c++用这个库做http服务端很简单,只需要实现具体接口就行
Server svr;
svr.Get("/hi", [](const Request & /*req*/, Response &res) {
res.set_content("Hello World!\n", "text/plain");
});
svr.listen("localhost", 8080);
3、调用get其实就是把请求url与实现方法做一个绑定,并且保存到get_handlers_
inline Server &Server::Get(const std::string &pattern, Handler handler) {
get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
return *this;
}
4、接口调用路径是这样:
listen--->listen_internal--->process_and_close_socket--->process_request--->routing--->dispatch_request
主线程listen--->listen_internal函数里面有一个循环,不断地select,accept请求,把所有请求的sockect都封装为一个lambda,放到线程池的任务队列中。
在线程池中调用process_and_close_socket,然后在process_request中接收数据得到request,然后在routing中调用dispatch_request,dispatch_request中执行get方法配置的实际方法体。
5、线程池
5.1 线程池线程数量CPPHTTPLIB_THREAD_POOL_COUNT 根据CPU核数和常数8来判断
#define CPPHTTPLIB_THREAD_POOL_COUNT \
((std::max)(8u, std::thread::hardware_concurrency() > 0 \
? std::thread::hardware_concurrency() - 1 \
: 0))
5.2 线程池变量,使用vector保存线程,list保存任务,线程同步使用了mutex和condition_variable
friend struct worker;
std::vector<std::thread> threads_;
std::list<std::function<void()>> jobs_;
bool shutdown_;
size_t max_queued_requests_ = 0;
std::condition_variable cond_;
std::mutex mutex_;
5.3 线程池构造函数中,创建多个线程,
explicit ThreadPool(size_t n, size_t mqr = 0)
: shutdown_(false), max_queued_requests_(mqr) {
while (n) {
threads_.emplace_back(worker(*this));
n--;
}
}
threads_.emplace_back(worker(*this));
这行代码的作用是:
- 创建一个
worker
对象,并将当前服务器对象的引用(*this
)传递给它。 - 在
threads_
容器中直接构造一个std::thread
对象,并启动它执行worker
对象的operator()
。
5.4 使用enqueue方法添加任务,任务是一个可执行对象function,这里是一个lambda表达式:
task_queue->enqueue(
[this, sock]() { process_and_close_socket(sock); })
bool enqueue(std::function<void()> fn) override {
{
std::unique_lock<std::mutex> lock(mutex_);
if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {
return false;
}
jobs_.push_back(std::move(fn));
}
cond_.notify_one();
return true;
}
5.5 work是一个结构体,是ThreadPool的友元,可以方法ThreadPool的私有成员。有一个仿函数,在仿函数中循环执行任务。也就是服务的所有请求都在这里执行。
struct worker {
explicit worker(ThreadPool &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 = pool_.jobs_.front();
pool_.jobs_.pop_front();
}
assert(true == static_cast<bool>(fn));
fn();
}
}
ThreadPool &pool_;
};
总的来说,httplib通过主线程listen来监听请求,使用select或者poll来接受请求,使用线程池处理接收数据,执行用户配置的执行函数。