请求处理简单框架
#ifndef _OTA_REQUEST_HANDLER_H_ #define _OTA_REQUEST_HANDLER_H_ #include "type_def.h" #include "gsl/span" #include "boost/asio.hpp" #include "zros/app_log/log.h" #include <thread> #include <memory> #include <functional> #include <vector> template<class REQ_CMD_TYPE, class REQ_TYPE, class RES_TYPE> bool process_req(const REQ_CMD_TYPE& req_cmd, gsl::span<const uint8_t> req_datas, std::vector<uint8_t>& resp_datas) { REQ_TYPE req_data; size_t data_length = 0; auto parse_error = parse_req_data(req_cmd, req_datas, req_data, data_length); if (parse_error != parse_error_type::OK) { return false; } RES_TYPE handle_result; auto err_code = handle_req(req_data, handle_result); resp_datas = make_response_frame(req_cmd, err_code, to_byte_array(handle_result)); return true; } template<class REQ_CMD_TYPE> class request_handler { public: using req_cmd_handler_pair = request_cmd_handler_pair<REQ_CMD_TYPE>; using data_send_func = std::function<void(const std::vector<uint8_t>&)>; using error_handler_func = std::function<void()>; using req_handle_func = std::function<void(const REQ_CMD_TYPE&)>; request_handler(data_send_func data_sender, error_handler_func error_handler, req_handle_func before_handle_req_func = nullptr, req_handle_func after_handle_req_func = nullptr) : m_is_stop(false), m_data_sender(data_sender), m_error_handler(error_handler), m_before_handle_req_func(before_handle_req_func), m_after_handle_req_func(after_handle_req_func), m_work(new boost::asio::io_service::work(m_io_service)) { get_req_cmd_handlers(m_request_cmd_handler); m_work_thread = std::thread([&](){ m_io_service.run(); }); } ~request_handler() { //assert(m_is_stop && "not stop request handler before destruct it!"); if (!m_is_stop) { zinfo("not stop request handler before destruct it!"); stop(); } } request_handler(request_handler &&) = delete; request_handler(const request_handler &) = delete; request_handler &operator=(request_handler &&) = delete; request_handler &operator=(const request_handler &) = delete; void handle_request(gsl::span<const uint8_t> datas) { std::vector<uint8_t> local_datas{datas.begin(), datas.end()}; m_io_service.post([local_datas, this](){ zdebug("receive %d datas", local_datas.size()); m_buffer.insert(m_buffer.end(), local_datas.begin(), local_datas.end()); handle_req_impl(); }); } void stop() { m_is_stop = true; m_work.reset(); m_io_service.stop(); m_work_thread.join(); } private: void handle_error() { if (m_error_handler) { m_error_handler(); } } void before_handle(const REQ_CMD_TYPE& req_cmd) { if (m_before_handle_req_func) { m_before_handle_req_func(req_cmd); } } void after_handle(const REQ_CMD_TYPE& req_cmd) { if (m_after_handle_req_func) { m_after_handle_req_func(req_cmd); } } void handle_req_impl() { while (true) { if (m_is_stop || m_buffer.empty()) { zdebug("handle_req_impl return directly\n"); return; } REQ_CMD_TYPE req_cmd; size_t req_cmd_length = 0; gsl::span<const uint8_t> one_frame; auto parse_res = parse_one_frame(m_buffer, one_frame, req_cmd); if (parse_res == parse_error_type::DATA_ERROR) { zinfo("parse_one_frame return data error"); m_data_sender(make_invalid_cmd_resp_frame(req_cmd)); m_buffer.clear(); handle_error(); return; } if (parse_res == parse_error_type::DATA_NOT_ENOUGH) { zinfo("parse_one_frame return data not enouth"); return; } parse_res = parse_req_cmd(one_frame, req_cmd, req_cmd_length); if (parse_res == parse_error_type::DATA_ERROR) { zinfo("peek_req_cmd return data error"); m_data_sender(make_invalid_cmd_resp_frame(req_cmd)); m_buffer.erase(m_buffer.begin(), m_buffer.begin()+one_frame.size()); continue; } zdebug("get cmd length: %d,cmd_handler num:%d,", req_cmd_length,m_request_cmd_handler.size()); auto ite = std::find_if(m_request_cmd_handler.begin(), m_request_cmd_handler.end(), [&req_cmd](const req_cmd_handler_pair& cmd_handler_pair){ return (cmd_handler_pair.cmd == req_cmd); }); if (m_request_cmd_handler.end() == ite) { zdebug("cmd_handler num:%d",m_request_cmd_handler.size()); m_data_sender(make_invalid_cmd_resp_frame(req_cmd)); m_buffer.erase(m_buffer.begin(), m_buffer.begin()+one_frame.size()); handle_error(); return; } before_handle(req_cmd); std::vector<uint8_t> resp_datas; auto res = ite->handler(req_cmd, gsl::span<const uint8_t>(one_frame.data()+req_cmd_length, one_frame.size()-req_cmd_length), resp_datas); m_buffer.erase(m_buffer.begin(), m_buffer.begin()+one_frame.size()); after_handle(req_cmd); if (!res) { m_data_sender(make_invalid_cmd_resp_frame(req_cmd)); handle_error(); return; } if (!resp_datas.empty()) { m_data_sender(resp_datas); } } } private: bool m_is_stop; std::vector<uint8_t> m_buffer; data_send_func m_data_sender; error_handler_func m_error_handler; req_handle_func m_before_handle_req_func; req_handle_func m_after_handle_req_func; std::thread m_work_thread; boost::asio::io_service m_io_service; std::unique_ptr<boost::asio::io_service::work> m_work; std::vector<req_cmd_handler_pair> m_request_cmd_handler; }; #endif // !_OTA_REQUEST_HANDLER_H_
如上代码:
从整体结构开始看起,确定这是一个头文件,定义了模板类和函数,用于处理服务请求。
首先,代码的开头和结尾使用了头文件保护宏#ifndef _OTA_REQUEST_HANDLER_H_和#define等,防止重复包含。接下来包含了一些头文件,比如type_def.h、GSL的span、Boost.Asio库、日志库以及一些标准库头文件,比如线程、内存、函数式、向量等。这些依赖项表明代码可能涉及异步IO操作、内存管理、多线程以及数据处理。
接下来看第一个模板函数process_req,它接受请求命令类型、请求数据类型、响应数据类型,以及请求数据和响应数据的容器。函数内部首先解析请求数据,如果解析成功,则处理请求并生成响应数据。这个函数似乎是一个通用的请求处理流程,可能被不同的请求处理实例复用。
然后看到request_handler类,这是一个模板类,模板参数是REQ_CMD_TYPE。这个类的作用处理具体的请求,管理请求的处理流程,包括数据的接收、解析、处理以及响应发送。类中有几个重要的类型别名,比如req_cmd_handler_pair、data_send_func等,这些可能用于定义回调函数和处理函数对。
构造函数接收几个函数对象:data_sender用于发送响应数据,error_handler处理错误,before和after处理请求前后的回调。构造函数中初始化了成员变量,并启动了一个工作线程来运行Boost.Asio的io_service,这通常用于异步任务处理。析构函数则负责停止服务并等待线程结束,确保资源正确释放。
成员函数handle_request接收一个数据span,将其拷贝到本地vector后,通过io_service.post投递到工作线程中处理,这保证了数据处理的线程安全。数据被添加到m_buffer中,随后调用handle_req_impl处理请求。
handle_req_impl函数是核心处理逻辑。它在一个循环中解析缓冲区中的数据,解析出请求命令,查找对应的处理函数,处理请求,生成响应,并发送响应。过程中处理可能的错误情况,比如数据不足或无效命令,并调用相应的错误处理回调。
在解析请求帧时,调用了parse_one_frame函数,该函数可能负责从原始数据中提取出一个完整的请求帧。如果解析失败,会发送无效命令的响应,并清空缓冲区。如果找到对应的命令处理函数,则调用process_req来处理具体请求,并生成响应数据。
错误处理部分,如handle_error函数,会调用用户提供的错误处理回调。而before_handle和after_handle分别在处理请求前后执行用户定义的操作,比如日志记录或预处理。
整个类使用了Boost.Asio的io_service来实现异步处理,通过工作线程来执行实际的数据处理任务,避免阻塞主线程。m_work对象用于保持io_service的运行,直到显式停止。
需要注意的是,代码中的一些函数如parse_req_data、handle_req、make_response_frame等并未在此头文件中定义,可能在别处实现。此外,m_request_cmd_handler通过get_req_cmd_handlers函数初始化,这个函数可能负责填充不同命令对应的处理函数。
可能存在的一些问题或需要注意的点包括:
1. 线程安全性:尽管数据通过io_service.post投递到工作线程,但需要确保所有对m_buffer和其他成员变量的访问都在同一线程内,避免竞态条件。这里由于所有处理都在io_service所在的线程中进行,应该是线程安全的。
2. 数据解析的正确性:parse_one_frame和parse_req_cmd的实现是否正确,是否能够正确分割和解析请求帧,否则可能导致数据错误或处理不完整。
3. 资源管理:在析构函数中,如果未正确停止handler,可能导致资源泄漏或线程未正确结束。代码中在析构时检查m_is_stop,并在必要时调用stop(),确保资源释放。
4. 错误处理机制:当解析或处理失败时,是否能够正确清理缓冲区并通知上层,这里通过发送错误响应并调用错误处理回调来实现。
5. 性能问题:频繁的数据拷贝(如handle_request中将数据复制到local_datas)可能影响性能,需要评估是否必要。此处可能为了避免数据在异步处理中被修改,所以进行拷贝,但若数据量大,可能需优化。
6. Boost.Asio的使用:是否正确管理io_service的生命周期,work对象的创建和销毁时机是否正确。这里在构造函数中创建work,防止io_service在没有任务时停止,stop()时释放work,允许io_service正常结束。
总结来说,这个request_handler类是一个基于Boost.Asio的异步请求处理器,支持多种命令类型,通过注册处理函数来处理不同的请求,并能够发送响应和错误处理。适用于需要异步处理网络请求或类似IO密集型任务的场景。
这段代码实现了一个基于模板的异步请求处理框架,主要用于处理OTA(空中下载)请求。以下是对其结构和功能的解析:
核心组件
-
模板函数 process_req
-
功能:通用请求处理流程。
-
输入:请求命令、请求数据、响应数据容器。
-
流程:
-
解析请求数据(
parse_req_data)。 -
若解析成功,调用请求处理函数(
handle_req)。 -
生成响应帧(
make_response_frame)。
-
-
返回值:布尔值表示处理成功与否。
-
-
模板类 request_handler
-
功能:管理请求的异步处理流程。
-
模板参数:
REQ_CMD_TYPE(请求命令类型)。
-
类成员与关键设计
-
类型别名
-
req_cmd_handler_pair:命令与处理函数的绑定。 -
data_send_func:发送响应数据的回调。 -
error_handler_func:错误处理回调。 -
req_handle_func:请求处理前后的钩子函数。
-
-
构造函数
-
接收数据发送、错误处理、预处理和后处理回调。
-
初始化 Boost.Asio 的
io_service和工作线程,确保异步任务执行。
-
-
析构函数
-
确保安全停止处理线程,避免资源泄漏。
-
-
核心方法
-
handle_request:接收原始数据,投递到工作线程处理。
-
stop:停止服务并等待线程结束。
-
handle_req_impl:实际解析和处理请求的核心逻辑。
-
异步处理机制
-
Boost.Asio 集成
-
使用
io_service管理异步任务队列。 -
work对象保持io_service持续运行,直到显式停止。 -
工作线程通过
io_service.run()执行任务。
-
-
线程安全
-
数据通过
io_service.post提交到工作线程,确保对缓冲区的操作单线程执行,避免竞态条件。
-
请求处理流程
-
数据接收
-
handle_request接收数据并拷贝到本地,通过io_service投递到工作线程。
-
-
数据解析
-
parse_one_frame:从原始缓冲区提取完整请求帧。
-
parse_req_cmd:解析请求命令和长度。
-
-
处理函数查找
-
在注册的命令处理列表中查找匹配项(
m_request_cmd_handler)。
-
-
请求执行
-
调用预处理钩子(
before_handle)。 -
执行对应的处理函数,生成响应数据。
-
调用后处理钩子(
after_handle)。
-
-
响应发送
-
通过
m_data_sender回调发送响应数据。
-
-
错误处理
-
解析或处理失败时,发送无效命令响应并触发错误回调。
-
关键依赖
-
GSL Span:用于安全的数据视图操作,避免不必要的拷贝。
-
Boost.Asio:提供异步IO和任务调度能力。
-
自定义日志库:通过
zinfo和zdebug记录日志。
潜在问题与优化
-
数据拷贝开销
-
handle_request中将数据拷贝到local_datas可能影响性能,需评估是否必要。
-
-
错误恢复
-
遇到数据错误时直接清空缓冲区,可能导致后续合法数据被丢弃,需考虑更精细的错误恢复策略。
-
-
处理函数注册
-
get_req_cmd_handlers的实现未展示,需确保正确注册所有命令处理函数。
-
-
线程管理
-
确保
stop()被正确调用,避免析构时资源未释放。
-
应用场景
适用于需要异步处理网络请求、支持多种命令类型的场景,例如:
-
固件升级服务。
-
物联网设备的远程控制。
-
高并发请求处理的中间件。
总结
该代码通过模板化和异步设计,提供了灵活的请求处理框架,支持自定义命令解析、处理逻辑和回调,结合 Boost.Asio 实现高效的任务调度,适合需要可靠异步处理的嵌入式或服务器端应用。

浙公网安备 33010602011771号