1. 程式人生 > 實用技巧 >cinatra中文開發文件

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() << "\n";
            return;
        }

        std::cout << data.resp_body << "\n"; //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() << "\n";
                return;
            }

            std::cout << "finished download\n";
        });
    }

    {
        auto client = cinatra::client_factory::instance().new_client();
        client->download(uri, [](auto ec, auto data) {
            if (ec) {
                std::cout << ec.message() << "\n";
                return;
            }

            if (data.empty()) {
                std::cout << "finished all \n";
            }
            else {
                std::cout << data.size() << "\n";
            }
        });
    }
}

效能測試

測試用例:

注意事項

websocket的業務函式是會多次進入的,因此寫業務邏輯的時候需要注意,推薦按照示例中的方式去做。

cinatra目前剛開始在生產環境中使用, 還處於開發完善階段,可能還有一些bug,因此不建議現階段直接用於生產環境,建議先在測試環境下試用。

試用沒問題了再在生產環境中使用,試用過程中發現了問題請及時提issue反饋或者郵件聯絡我。

測試和使用穩定之後cinatra會發布正式版。

聯絡方式

[email protected]

qq群:340713904

http://purecpp.org/

https://github.com/qicosmos/cinatra