cinatra是一个高性能易用的http框架,它是用modern c++(c++17)开发的,它的目标是提供一个快速开发的c++ http框架。它的主要特点如下:

  1. 统一而简单的接口
  2. header-only
  3. 跨平台
  4. 高效
  5. 支持面向切面编程

cinatra目前支持了http1.1/1.0和websocket, 你可以用它轻易地开发一个http服务器,比如常见的数据库访问服务器、文件上传下载服务器、实时消息推送服务器,你也可以基于cinatra开发一个mqtt服务器。

如何使用

编译依赖

cinatra是基于boost.asio开发的,所以需要boost库,同时也需要支持c++17的编译器,依赖项:

  1. boost.asio
  2. c++17编译器(gcc7.2,clang4.0, vs2017 update15.5)

使用

cinatra是header-only的,直接引用头文件既可。

快速示例

示例1:一个简单的hello world

#include "http_server.hpp"
using namespace cinatra; int main() {
http_server server(std::thread::hardware_concurrency());
server.listen("0.0.0.0", "8080");
server.set_http_handler<GET, POST>("/", [](const request& req, response& res) {
res.set_status_and_content(status_type::ok, "hello world");
}); server.run();
return 0;
}

5行代码就可以实现一个简单http服务器了,用户不需要关注多少细节,直接写业务逻辑就行了。

示例2:展示如何取header和query以及错误返回

#include "http_server.hpp"
using namespace cinatra; int main() {
http_server server(std::thread::hardware_concurrency());
server.listen("0.0.0.0", "8080");
server.set_http_handler<GET, POST>("/test", [](const request& req, response& res) {
auto name = req.get_header_value("name");
if (name.empty()) {
res.set_status_and_content(status_type::bad_request, "no name");
return;
} auto id = req.get_query_value("id");
if (id.empty()) {
res.set_status_and_content(status_type::bad_request);
return;
} res.set_status_and_content(status_type::ok, "hello world");
}); server.run();
return 0;
}

示例3:面向切面的http服务器

#include "http_server.hpp"
using namespace cinatra; //日志切面
struct log_t
{
bool before(const request& req, response& res) {
std::cout << "before log" << std::endl;
return true;
} bool after(const request& req, response& res) {
std::cout << "after log" << std::endl;
return true;
}
}; //校验的切面
struct check {
bool before(const request& req, response& res) {
std::cout << "before check" << std::endl;
if (req.get_header_value("name").empty()) {
res.set_status_and_content(status_type::bad_request);
return false;
} return true;
} bool after(const request& req, response& res) {
std::cout << "after check" << std::endl;
return true;
}
}; int main() {
http_server server(std::thread::hardware_concurrency());
server.listen("0.0.0.0", "8080");
server.set_http_handler<GET, POST>("/aspect", [](const request& req, response& res) {
res.set_status_and_content(status_type::ok, "hello world");
}, check{}, log_t{}); server.run();
return 0;
}

本例中有两个切面,一个校验http请求的切面,一个是日志切面,这个切面用户可以根据需求任意增加。本例会先检查http请求的合法性,如果不合法就会返回bad request,合法就会进入下一个切面,即日志切面,六安广播电视大学日志切面会打印出一个before表示进入业务逻辑之前的处理,池州先锋网业务逻辑完成之后会打印after表示业务逻辑结束之后的处理。

示例4:文件上传

cinatra目前支持了multipart和octet-stream格式的上传。

multipart文件上传

#include <atomic>
#include "http_server.hpp"
using namespace cinatra; int main() {
http_server server(std::thread::hardware_concurrency());
server.listen("0.0.0.0", "8080"); std::atomic_int n = 0;
//http upload(multipart)
server.set_http_handler<GET, POST>("/upload_multipart", [&n](const request& req, response& res) {
assert(req.get_http_type() == http_type::multipart);
auto state = req.get_state();
switch (state)
{
case cinatra::data_proc_state::data_begin:
{
auto file_name_s = req.get_multipart_file_name();
auto extension = get_extension(file_name_s); std::string file_name = std::to_string(n++) + std::string(extension.data(), extension.length());
auto file = std::make_shared<std::ofstream>(file_name, std::ios::binary);
if (!file->is_open()) {
res.set_continue(false);
return;
}
req.get_conn()->set_tag(file);
}
break;
case cinatra::data_proc_state::data_continue:
{
if (!res.need_continue()) {
return;
} auto file = std::any_cast<std::shared_ptr<std::ofstream>>(req.get_conn()->get_tag());
auto part_data = req.get_part_data();
file->write(part_data.data(), part_data.length());
}
break;
case cinatra::data_proc_state::data_end:
{
std::cout << "one file finished" << std::endl;
}
break;
case cinatra::data_proc_state::data_all_end:
{
//all the upstream end
std::cout << "all files finished" << std::endl;
res.set_status_and_content(status_type::ok);
}
break;
case cinatra::data_proc_state::data_error:
{
//network error
}
break;
}
}); server.run();
return 0;
}

短短几行代码就可以实现一个http文件上传的服务器了,包含了异常处理和错误处理。

octet-stream文件上传

#include <atomic>
#include "http_server.hpp"
using namespace cinatra; int main() {
http_server server(std::thread::hardware_concurrency());
server.listen("0.0.0.0", "8080"); std::atomic_int n = 0;
//http upload(octet-stream)
server.set_http_handler<GET, POST>("/upload_octet_stream", [&n](const request& req, response& res) {
assert(req.get_http_type() == http_type::octet_stream);
auto state = req.get_state();
switch (state)
{
case cinatra::data_proc_state::data_begin:
{
std::string file_name = std::to_string(n++);;
auto file = std::make_shared<std::ofstream>(file_name, std::ios::binary);
if (!file->is_open()) {
res.set_continue(false);
return;
}
req.get_conn()->set_tag(file);
}
break;
case cinatra::data_proc_state::data_continue:
{
if (!res.need_continue()) {
return;
} auto file = std::any_cast<std::shared_ptr<std::ofstream>>(req.get_conn()->get_tag());
auto part_data = req.get_part_data();
file->write(part_data.data(), part_data.length());
}
break;
case cinatra::data_proc_state::data_end:
{
std::cout << "one file finished" << std::endl;
}
break;
case cinatra::data_proc_state::data_error:
{
//network error
}
break;
}
}); server.run();
return 0;
}

示例5:文件下载

#include "http_server.hpp"
using namespace cinatra; int main() {
http_server server(std::thread::hardware_concurrency());
server.listen("0.0.0.0", "8080"); //http download(chunked)
server.set_http_handler<GET, POST>("/download_chunked", [](const request& req, response& res) {
auto state = req.get_state();
switch (state)
{
case cinatra::data_proc_state::data_begin:
{
std::string filename = "2.jpg";
auto in = std::make_shared<std::ifstream>(filename, std::ios::binary);
if (!in->is_open()) {
req.get_conn()->on_error();
return;
} auto conn = req.get_conn();
conn->set_tag(in);
auto extension = get_extension(filename.data());
auto mime = get_mime_type(extension);
conn->write_chunked_header(mime);
}
break;
case cinatra::data_proc_state::data_continue:
{
auto conn = req.get_conn();
auto in = std::any_cast<std::shared_ptr<std::ifstream>>(conn->get_tag()); std::string str;
const size_t len = 2*1024;
str.resize(len); in->read(&str[0], len);
size_t read_len = in->gcount();
if (read_len != len) {
str.resize(read_len);
}
bool eof = (read_len==0|| read_len != len);
conn->write_chunked_data(std::move(str), eof);
}
break;
case cinatra::data_proc_state::data_end:
{
std::cout << "chunked send finish" << std::endl;
}
break;
case cinatra::data_proc_state::data_error:
{
//network error
}
break;
}
}); server.run();
return 0;
}

示例6:websocket

#include "http_server.hpp"
using namespace cinatra; int main() {
http_server server(std::thread::hardware_concurrency());
server.listen("0.0.0.0", "8080"); //web socket
server.set_http_handler<GET, POST>("/ws", [](const request& req, response& res) {
assert(req.get_http_type() == http_type::websocket);
auto state = req.get_state();
switch (state)
{
case cinatra::data_proc_state::data_begin:
{
std::cout << "websocket start" << std::endl;
}
break;
case cinatra::data_proc_state::data_continue:
{
auto part_data = req.get_part_data();
//echo
req.get_conn()->send_ws_msg(std::string(part_data.data(), part_data.length()));
std::cout << part_data.data() << std::endl;
}
break;
case cinatra::data_proc_state::data_close:
{
std::cout << "websocket close" << std::endl;
}
break;
case cinatra::data_proc_state::data_error:
{
std::cout << "network error" << std::endl;
}
break;
}
}); server.run();
return 0;
}

性能测试

测试用例:

ab测试:ab -c100 -n5000 127.0.0.1:8080/

服务器返回一个hello。

在一个8核心16G的云主机上测试,qps在9000-13000之间。

对比测试

通过ab测试和boost.beast做对比,二者qps相当,大概是因为二者都是基于boost.asio开发的的原因。cinatra目前还没做专门的性能优化,还有提升空间。

注意事项

文件上传下载,websocket的业务函数是会多次进入的,因此写业务逻辑的时候需要注意,推荐按照示例中的方式去做。

cinatra目前刚开始在生产环境中使用, 还处于开发完善阶段,可能还有一些bug,因此不建议现阶段直接用于生产环境,建议先在测试环境下试用。

试用没问题了再在生产环境中使用,试用过程中发现了问题请及时提issue反馈或者邮件联系我。

测试和使用稳定之后cinatra会发布正式版。

roadmap

  1. 支持ssl
  2. 支持断点续传
  3. 支持session和cookie
  4. 接口优化、性能优化

我希望cinatra有越来越多的人使用并喜欢它,也希望在在使用过程中越来越完善,变成一个强大易用、快速开发的http框架,欢迎大家积极参与cinatra项目,可以提issue也可以发邮件提建议,也可以提pr,形式不限。

这次重构的cinatra几乎是重写了一遍,代码比之前的少了30%以上,接口统一了,http和业务分离,具备更好的扩展性和可维护性。

cinatra--一个高效易用的c++ http框架的更多相关文章

  1. SpringMVC整合Shiro,Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能

    SpringMVC整合Shiro,Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能. 第一步:配置web.xml <!-- 配置Shiro过滤器,先让Shiro ...

  2. CSharpGL(0)一个易学易用的C#版OpenGL

    +BIT祝威+悄悄在此留下版了个权的信说: CSharpGL(0)一个易学易用的C#版OpenGL CSharpGL是我受到SharpGL的启发,在整理了SharpGL,GLM,SharpFont等开 ...

  3. 分享一个简单易用的RPC开源项目—Tatala

    http://zijan.iteye.com/blog/2041894 这个项目最早(2008年)是用于一个网络游戏的Cache Server,以及一个电子商务的Web Session服务.后来不断增 ...

  4. 成为一个高效的web开发人员,只需要三步

    想成为一名专业的web开发人员并不像你想象的那么容易,开发人员在开发自己的web项目时常常需要牢记很多东西,他们要不断寻找新理念,新创意,在特定时间内开发出高质量的产品,一名优秀的程序员必须明白时间的 ...

  5. 一个简单易用的容器管理平台-Humpback

    什么是Humpback? 在回答这个问题前,我们得先了解下什么的 Docker(哦,现在叫 Moby,文中还是继续称 Docker). 在 Docker-百度百科 中,对 Docker 已经解释得很清 ...

  6. 想成为一个高效的Web开发者吗?来看看大牛分享的经验吧~ #精选JAVASCRIPT前端开发

    想成为一个高效的Web开发者吗?来看看大牛分享的经验吧~ 作为一个软(ku)件(bi)工(de)程(ma)师(nong),你有没有觉得做什么事都没时间?没时间学习新东西,没时间去回顾.整理原来写的烂代 ...

  7. [转载]一个高效简洁的Aseprite to Unity导入工具

    原文链接 https://zhuanlan.zhihu.com/p/28644268  期待原作者上传至AssetStore. 今天,我的第一个 Unity 插件 MetaSprite 正式发布了它的 ...

  8. Android:一个高效的UI才是一个拉风的UI(二)

    趁今晚老大不在偷偷早下班,所以有时间继续跟大伙扯扯UI设计之痛,也算一个是对上篇<Android:一个高效的UI才是一个拉风的UI(一)>的完整补充吧.写得不好的话大家尽管拍砖~(来!砸死 ...

  9. 发布一个高效的JavaScript分析、压缩工具 JavaScript Analyser

    发布一个高效的JavaScript分析.压缩工具 JavaScript Analyser 先发一段脚本压缩示例,展示一下JSA语法压缩和优化功能. try { //xxxx(); } catch (e ...

随机推荐

  1. softmax sigmoid

    softmax和sigmoid实际上都是属于logistic regression,sigmoid是二分类的lr,拟合Bernoulli distribution(二项分布):0softmax是多分类 ...

  2. centos7安装python3和ipython

    CentOS7下默认系统自带python2.X的版本,这个版本被系统很多程序所依赖,所以不建议删除,如果使用最新的Python3那么我们知道编译安装源码包和系统默认包之间是没有任何影响的,所以可以安装 ...

  3. hadoop中compare函数

    在看hadoop  的二次排序的时候,改写了下, 加了第三个参数,  本来以为是在 public int compareTo(IntPair o) { System.out.println(" ...

  4. leetCode没那么难啦 in Java (一)

    前言    感觉写博客是一个很耗心力的东西T_T,简单的写了似乎没什么用,复杂的三言两语也只能讲个大概,呸呸...怎么能有这些消极思想呢QAQ!那想来想去,先开一个leetcode的坑,虽然已经工作了 ...

  5. Python——配置环境的导出与导入

    导出Python环境安装包[root@bogon ~]# pip freeze > packages.txt 这将会创建一个 packages.txt文件,其中包含了当前环境中所有包及各自的版本 ...

  6. Shell简介:1分钟理解什么是Shell 脚本语言 解释器 以及编译器和编译语言

    Shell简介:1分钟理解什么是Shell 脚本语言 解释器 以及编译器和编译语言 现在我们使用的操作系统(Windows.Mac OS.Android.iOS 等)都是带图形界面的,简单直观,容易上 ...

  7. 使用android ndk编译x86 so在linux下使用的问题

    一直以为android ndk编译x86 so库可以在linxu下运行,结果我试了几次都行不通.后来想了一下,android ndk编译的库应该只能在android设备或模拟器上运行才有效,后来改用 ...

  8. mysql 自增主键为什么不是连续的?

    由于自增主键可以让主键索引尽量地保持递增顺序插入,避免了页分裂,因此索引更紧凑 MyISAM 引擎的自增值保存在数据文件中 nnoDB 引擎的自增值,其实是保存在了内存里,并且到了 MySQL 8.0 ...

  9. 文件/etc/passwd,/etc/shadow,/etc/group

    文件/etc/passwd /etc/shadow /etc/group 计算资源的使用(并不是所有的人都可以用这台计算机的) 权限:访问资源的的能力. 用户:获取资源或者权限的凭证. 用户的容器:关 ...

  10. GCJ:2008 Round1AA-Minimum Scalar Product(有序数组倒序乘积和最小)

    题目链接:https://code.google.com/codejam/contest/32016/dashboard#s=p0 Minimum Scalar Product This contes ...