zoukankan      html  css  js  c++  java
  • cinatra中文开发文档

    2020年8月5日09:40:20

    cinatra--一个高效易用的c++ http框架

    English | 中文

    目录

    cinatra简介

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

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

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

    谁在用cinatra

    cinatra目前被很多公司在使用,在这里可以看到谁在用cinatra.

    如何使用

    编译依赖

    cinatra是基于boost.asio开发的,所以需要boost库。不过,cinatra同时也支持了ASIO_STANDALONE,你不必一定需要boost库。

    cinatra需要的依赖项:

    1. C++17 编译器 (gcc 7.2, clang 4.0, Visual Studio 2017 update 15.5,或者更高的版本)
    2. Boost.Asio(或者独立的 Asio)
    3. Boost.System

    使用

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

    快速示例

    示例1:一个简单的hello world

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

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

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

    #include "cinatra.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", [](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 "cinatra.hpp"
    using namespace cinatra;
    
    //日志切面
    struct log_t
    {
    	bool before(request& req, response& res) {
    		std::cout << "before log" << std::endl;
    		return true;
    	}
    
    	bool after(request& req, response& res) {
    		std::cout << "after log" << std::endl;
    		return true;
    	}
    };
    
    //校验的切面
    struct check {
    	bool before(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(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", [](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 "cinatra.hpp"
    using namespace cinatra;
    
    int main() {
    	http_server server(std::thread::hardware_concurrency());
    	server.listen("0.0.0.0", "8080");
    
    	//http upload(multipart)
    	server.set_http_handler<GET, POST>("/upload_multipart", [](request& req, response& res) {
    		assert(req.get_content_type() == content_type::multipart);
    		
    		auto& files = req.get_upload_files();
    		for (auto& file : files) {
    			std::cout << file.get_file_path() << " " << file.get_file_size() << std::endl;
    		}
    
    		res.set_status_and_content(status_type::ok, "multipart finished");
    	});
    
    	server.run();
    	return 0;
    }
    

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

    octet-stream文件上传

    #include <atomic>
    #include "cinatra.hpp"
    using namespace cinatra;
    
    int main() {
    	http_server server(std::thread::hardware_concurrency());
    	server.listen("0.0.0.0", "8080");
    
    	//http upload(octet-stream)
    	server.set_http_handler<GET, POST>("/upload_octet_stream", [](request& req, response& res) {
    		assert(req.get_content_type() == content_type::octet_stream);
    		auto& files = req.get_upload_files();
    		for (auto& file : files) {
    			std::cout << file.get_file_path() << " " << file.get_file_size() << std::endl;
    		}
    
    		res.set_status_and_content(status_type::ok, "octet-stream finished");
    	});
    
    	server.run();
    	return 0;
    }
    

    示例5:文件下载

    //chunked download
    //http://127.0.0.1:8080/assets/show.jpg
    //cinatra will send you the file, if the file is big file(more than 5M) the file will be downloaded by chunked. support continues download
    

    示例6:websocket

    #include "cinatra.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", [](request& req, response& res) {
    		assert(req.get_content_type() == content_type::websocket);
    
    		req.on(ws_open, [](request& req){
    			std::cout << "websocket start" << std::endl;
    		});
    
    		req.on(ws_message, [](request& req) {
    			auto part_data = req.get_part_data();
    			//echo
    			std::string str = std::string(part_data.data(), part_data.length());
    			req.get_conn()->send_ws_string(std::move(str));
    			std::cout << part_data.data() << std::endl;
    		});
    
    		req.on(ws_error, [](request& req) {
    			std::cout << "websocket pack error or network error" << std::endl;
    		});
    	});
    
    	server.run();
    	return 0;
    }
    

    示例7:io_service_inplace

    本代码演示如何使用io_service_inplace,然后自己控制http server的运行线程以及循环。
    使用 [http://[::1]:8080/close] (IPv6) 或者 [http://127.0.0.1:8080/close] (IPv4) 来关闭http server。

    #include "cinatra.hpp"
    using namespace cinatra;
    
    int main() {
    
    	bool is_running = true;
    	http_server_<io_service_inplace> server;
    	server.listen("8080");
    
    	server.set_http_handler<GET, POST>("/", [](request& req, response& res) {
    		res.set_status_and_content(status_type::ok, "hello world");
    	});
    
    	server.set_http_handler<GET, POST>("/close", [&](request& req, response& res) {
    		res.set_status_and_content(status_type::ok, "will close");
    
    		is_running = false;
    		server.stop();
    	});
    
    	while(is_running)
    		server.poll_one();
    
    	return 0;
    }
    

    cinatra客户端使用

    同步发get/post消息

    同步和异步发送接口都是返回response_data,它有4个字段分别是:网络错误码、http状态码、返回的消息、返回的header。

    void print(const response_data& result) {
        print(result.ec, result.status, result.resp_body, result.resp_headers.second);
    }
    
    void test_sync_client() {
        auto client = cinatra::client_factory::instance().new_client();
        std::string uri = "http://www.baidu.com";
        std::string uri1 = "http://cn.bing.com";
        std::string uri2 = "https://www.baidu.com";
        std::string uri3 = "https://cn.bing.com";
        
        response_data result = client->get(uri);
        print(result);
    
        response_data result1 = client->get(uri1);
        print(result1);
    
        print(client->post(uri, "hello"));
        print(client->post(uri1, "hello"));
    
    #ifdef CINATRA_ENABLE_SSL
        response_data result2 = client->get(uri2);
        print(result2);
    
        response_data result3 = client->get(uri3);
        print(result3);
    
        response_data result4 = client->get(uri3);
        print(result4);
    
        response_data result5 = client->get(uri2);
        print(result5);
    #endif
    }
    

    异步发get/post消息

    void test_async_client() {
        
        std::string uri = "http://www.baidu.com";
        std::string uri1 = "http://cn.bing.com";
        std::string uri2 = "https://www.baidu.com";
        std::string uri3 = "https://cn.bing.com";
    
        {
            auto client = cinatra::client_factory::instance().new_client();
            client->async_get(uri, [](response_data data) {
                print(data);
            });
        }
        
        {
            auto client = cinatra::client_factory::instance().new_client();
            client->async_get(uri1, [](response_data data) {
                print(data);
            });
        }
    
        {
            auto client = cinatra::client_factory::instance().new_client();
            client->async_post(uri, "hello", [](response_data data) {
                print(data);
            });
        }
    
    #ifdef CINATRA_ENABLE_SSL
        {
            auto client = cinatra::client_factory::instance().new_client();
            client->async_get(uri2, [](response_data data) {
                print(data);
            });
        }
    
        {
            auto client = cinatra::client_factory::instance().new_client();
            client->async_get(uri3, [](response_data data) {
                print(data);
            });
        }
    #endif
    }
    

    文件上传

    异步multipart文件上传。

    void test_upload() {
        std::string uri = "http://cn.bing.com/";
        auto client = cinatra::client_factory::instance().new_client();
        client->upload(uri, "boost_1_72_0.7z", [](response_data data) {
            if (data.ec) {
                std::cout << data.ec.message() << "
    ";
                return;
            }
    
            std::cout << data.resp_body << "
    "; //finished upload
        });
    }
    

    文件下载

    提供了两个异步chunked下载接口,一个是直接下载到文件,一个是chunk回调给用户,由用户自己去处理下载的chunk数据

    void test_download() {
        std::string uri = "http://www.httpwatch.com/httpgallery/chunked/chunkedimage.aspx";
    
        {
            auto client = cinatra::client_factory::instance().new_client();
            client->download(uri, "test.jpg", [](response_data data) {
                if (data.ec) {
                    std::cout << data.ec.message() << "
    ";
                    return;
                }
    
                std::cout << "finished download
    ";
            });
        }
    
        {
            auto client = cinatra::client_factory::instance().new_client();
            client->download(uri, [](auto ec, auto data) {
                if (ec) {
                    std::cout << ec.message() << "
    ";
                    return;
                }
    
                if (data.empty()) {
                    std::cout << "finished all 
    ";
                }
                else {
                    std::cout << data.size() << "
    ";
                }
            });
        }
    }
    
    

    性能测试

    测试用例:

    qps

    qps-pipeline

    注意事项

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

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

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

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

    联系方式

    purecpp@163.com

    qq群:340713904

    http://purecpp.org/

    https://github.com/qicosmos/cinatra

  • 相关阅读:
    Write an algorithm such that if an element in an MxN matrix is 0, its entire row and column is set to 0.
    旋转二维数组
    replace empty char with new string,unsafe method和native implementation的性能比较
    判断一字符串是否可以另一字符串重新排列而成
    移除重复字符的几个算法简单比较
    也来纠结一下字符串翻转
    判断重复字符存在:更有意义一点
    程序员常去网站汇总
    sublime
    针对程序集 'SqlServerTime' 的 ALTER ASSEMBLY 失败,因为程序集 'SqlServerTime' 未获授权(PERMISSION_SET = EXTERNAL_ACCESS)
  • 原文地址:https://www.cnblogs.com/zx-admin/p/13437860.html
Copyright © 2011-2022 走看看