首頁>技術>

一、簡介

libhv是一個類似於libevent、libev、libuv的基於C++的跨平臺網路庫,提供了更簡單的介面和更豐富的協議。

二、上手1. 克隆專案
git clone https://gitee.com/ithewei/libhv
2. 開始
cd libhv./getting_started.sh

該命令會編譯後自動執行一些測試命令。

編譯後提示執行

bin/httpd -c etc/httpd.conf -s restart -d

命令。

執行後,可以找開網頁:

http://localhost:8080/downloads/

三、 實現一個基本的http服務端
/* * sample http server * more detail see examples/httpd * */#include "HttpServer.h"int main() {    HV_MEMCHECK;    HttpService service;    service.GET("/ping", [](HttpRequest* req, HttpResponse* resp) {        resp->body = "pong";        return 200;    });    service.POST("/echo", [](HttpRequest* req, HttpResponse* resp) {        resp->content_type = req->content_type;        resp->body = req->body;        return 200;    });    http_server_t server;    server.port = 8080;    // uncomment to test multi-processes    // server.worker_processes = 4;    // uncomment to test multi-threads    // server.worker_threads = 4;    server.service = &service;#if 1    http_server_run(&server);#else    // test http_server_stop    http_server_run(&server, 0);    sleep(10);    http_server_stop(&server);#endif    return 0;}
四、實現一個web服務框架1. 程式碼說明

示例程式碼在exampels/httpd裡。

4.1.1 handler.h 控制器程式
#ifndef HV_HTTPD_HANDLER_H#define HV_HTTPD_HANDLER_H#include "HttpMessage.h"class Handler {public: 	// 資料流向    // preprocessor => handler => postprocessor    static int preprocessor(HttpRequest* req, HttpResponse* resp) {        // printf("%s:%d\n", req->client_addr.ip.c_str(), req->client_addr.port);        // printf("%s\n", req->Dump(true, true).c_str());        // if (req->content_type != APPLICATION_JSON) {        //     return response_status(resp, HTTP_STATUS_BAD_REQUEST);        // }        req->ParseBody();        resp->content_type = APPLICATION_JSON;#if 0        // authentication sample code        if (strcmp(req->path.c_str(), "/login") != 0) {            string token = req->GetHeader("token");            if (token.empty()) {                response_status(resp, 10011, "Miss token");                return HTTP_STATUS_UNAUTHORIZED;            }            else if (strcmp(token.c_str(), "abcdefg") != 0) {                response_status(resp, 10012, "Token wrong");                return HTTP_STATUS_UNAUTHORIZED;            }            return 0;        }#endif        return 0;    }    static int postprocessor(HttpRequest* req, HttpResponse* resp) {        // printf("%s\n", resp->Dump(true, true).c_str());        return 0;    }    static int sleep(HttpRequest* req, HttpResponse* resp) {        time_t start_time = time(NULL);        std::string strTime = req->GetParam("t");        if (!strTime.empty()) {            int sec = atoi(strTime.c_str());            if (sec > 0) {                hv_delay(sec*1000);            }        }        time_t end_time = time(NULL);        resp->Set("start_time", start_time);        resp->Set("end_time", end_time);        response_status(resp, 0, "OK");        return 200;    }    static int query(HttpRequest* req, HttpResponse* resp) {        // scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]        // ?query => HttpRequest::query_params        for (auto& param : req->query_params) {            resp->Set(param.first.c_str(), param.second);        }        response_status(resp, 0, "OK");        return 200;    }    static int kv(HttpRequest* req, HttpResponse* resp) {        if (req->content_type != APPLICATION_URLENCODED) {            return response_status(resp, HTTP_STATUS_BAD_REQUEST);        }        resp->content_type = APPLICATION_URLENCODED;        resp->kv = req->kv;        resp->kv["int"] = hv::to_string(123);        resp->kv["float"] = hv::to_string(3.14);        resp->kv["string"] = "hello";        return 200;    }    static int json(HttpRequest* req, HttpResponse* resp) {        if (req->content_type != APPLICATION_JSON) {            return response_status(resp, HTTP_STATUS_BAD_REQUEST);        }        resp->content_type = APPLICATION_JSON;        resp->json = req->json;        resp->json["int"] = 123;        resp->json["float"] = 3.14;        resp->json["string"] = "hello";        return 200;    }    static int form(HttpRequest* req, HttpResponse* resp) {        if (req->content_type != MULTIPART_FORM_DATA) {            return response_status(resp, HTTP_STATUS_BAD_REQUEST);        }        resp->content_type = MULTIPART_FORM_DATA;        resp->form = req->form;        resp->form["int"] = 123;        resp->form["float"] = 3.14;        resp->form["float"] = "hello";        // resp->form["file"] = FormData("test.jpg");        return 200;    }    static int test(HttpRequest* req, HttpResponse* resp) {        // bool b = req->Get<bool>("bool");        // int64_t n = req->Get<int64_t>("int");        // double f = req->Get<double>("float");        bool b = req->GetBool("bool");        int64_t n = req->GetInt("int");        double f = req->GetFloat("float");        string str = req->GetString("string");        resp->content_type = req->content_type;        resp->Set("bool", b);        resp->Set("int", n);        resp->Set("float", f);        resp->Set("string", str);        response_status(resp, 0, "OK");        return 200;    }    static int grpc(HttpRequest* req, HttpResponse* resp) {        if (req->content_type != APPLICATION_GRPC) {            return response_status(resp, HTTP_STATUS_BAD_REQUEST);        }        // parse protobuf        // ParseFromString(req->body);        // resp->content_type = APPLICATION_GRPC;        // serailize protobuf        // resp->body = SerializeAsString(xxx);        response_status(resp, 0, "OK");        return 200;    }    static int restful(HttpRequest* req, HttpResponse* resp) {        // RESTful /:field/ => HttpRequest::query_params        // path=/group/:group_name/user/:user_id        // string group_name = req->GetParam("group_name");        // string user_id = req->GetParam("user_id");        for (auto& param : req->query_params) {            resp->Set(param.first.c_str(), param.second);        }        response_status(resp, 0, "OK");        return 200;    }    static int login(HttpRequest* req, HttpResponse* resp) {        string username = req->GetString("username");        string password = req->GetString("password");        if (username.empty() || password.empty()) {            response_status(resp, 10001, "Miss username or password");            return HTTP_STATUS_BAD_REQUEST;        }        else if (strcmp(username.c_str(), "admin") != 0) {            response_status(resp, 10002, "Username not exist");            return HTTP_STATUS_BAD_REQUEST;        }        else if (strcmp(password.c_str(), "123456") != 0) {            response_status(resp, 10003, "Password wrong");            return HTTP_STATUS_BAD_REQUEST;        }        else {            resp->Set("token", "abcdefg");            response_status(resp, 0, "OK");            return HTTP_STATUS_OK;        }    }    static int upload(HttpRequest* req, HttpResponse* resp) {        if (req->content_type != MULTIPART_FORM_DATA) {            return response_status(resp, HTTP_STATUS_BAD_REQUEST);        }        FormData file = req->form["file"];        string filepath("html/uploads/");        filepath += file.filename;        FILE* fp = fopen(filepath.c_str(), "w");        if (fp) {            fwrite(file.content.data(), 1, file.content.size(), fp);            fclose(fp);        }        response_status(resp, 0, "OK");        return 200;    }private:    static int response_status(HttpResponse* resp, int code = 200, const char* message = NULL) {        resp->Set("code", code);        if (message == NULL) message = http_status_str((enum http_status)code);        resp->Set("message", message);        resp->DumpBody();        return code;    }};#endif // HV_HTTPD_HANDLER_H
4.1.2 httpd.cpp 主服務
#include "hv.h"#include "hmain.h"#include "iniparser.h"#include "HttpServer.h"#include "router.h"http_server_t   g_http_server;HttpService     g_http_service;static void print_version();static void print_help();static int  parse_confile(const char* confile);// short optionsstatic const char options[] = "hvc:ts:dp:";// long optionsstatic const option_t long_options[] = {    {'h', "help",       NO_ARGUMENT},    {'v', "version",    NO_ARGUMENT},    {'c', "confile",    REQUIRED_ARGUMENT},    {'t', "test",       NO_ARGUMENT},    {'s', "signal",     REQUIRED_ARGUMENT},    {'d', "daemon",     NO_ARGUMENT},    {'p', "port",       REQUIRED_ARGUMENT}};static const char detail_options[] = R"(  -h|--help                 Print this information  -v|--version              Print version  -c|--confile <confile>    Set configure file, default etc/{program}.conf  -t|--test                 Test configure file and exit  -s|--signal <signal>      Send <signal> to process,                            <signal>=[start,stop,restart,status,reload]  -d|--daemon               Daemonize  -p|--port <port>          Set listen port)";void print_version() {    printf("%s version %s\n", g_main_ctx.program_name, hv_compile_version());}void print_help() {    printf("Usage: %s [%s]\n", g_main_ctx.program_name, options);    printf("Options:\n%s\n", detail_options);}int parse_confile(const char* confile) {    IniParser ini;    int ret = ini.LoadFromFile(confile);    if (ret != 0) {        printf("Load confile [%s] failed: %d\n", confile, ret);        exit(-40);    }    // logfile    string str = ini.GetValue("logfile");    if (!str.empty()) {        strncpy(g_main_ctx.logfile, str.c_str(), sizeof(g_main_ctx.logfile));    }    hlog_set_file(g_main_ctx.logfile);    // loglevel    str = ini.GetValue("loglevel");    if (!str.empty()) {        hlog_set_level_by_str(str.c_str());    }    // log_filesize    str = ini.GetValue("log_filesize");    if (!str.empty()) {        hlog_set_max_filesize_by_str(str.c_str());    }    // log_remain_days    str = ini.GetValue("log_remain_days");    if (!str.empty()) {        hlog_set_remain_days(atoi(str.c_str()));    }    // log_fsync    str = ini.GetValue("log_fsync");    if (!str.empty()) {        logger_enable_fsync(hlog, getboolean(str.c_str()));    }    hlogi("%s version: %s", g_main_ctx.program_name, hv_compile_version());    hlog_fsync();    // worker_processes    int worker_processes = 0;    str = ini.GetValue("worker_processes");    if (str.size() != 0) {        if (strcmp(str.c_str(), "auto") == 0) {            worker_processes = get_ncpu();            hlogd("worker_processes=ncpu=%d", worker_processes);        }        else {            worker_processes = atoi(str.c_str());        }    }    g_http_server.worker_processes = LIMIT(0, worker_processes, MAXNUM_WORKER_PROCESSES);    // worker_threads    int worker_threads = ini.Get<int>("worker_threads");    g_http_server.worker_threads = LIMIT(0, worker_threads, 16);    // port    int port = 0;    const char* szPort = get_arg("p");    if (szPort) {        port = atoi(szPort);    }    if (port == 0) {        port = ini.Get<int>("port");    }    if (port == 0) {        printf("Please config listen port!\n");        exit(-10);    }    g_http_server.port = port;    // http server    // base_url    str = ini.GetValue("base_url");    if (str.size() != 0) {        g_http_service.base_url = str;    }    // document_root    str = ini.GetValue("document_root");    if (str.size() != 0) {        g_http_service.document_root = str;    }    // home_page    str = ini.GetValue("home_page");    if (str.size() != 0) {        g_http_service.home_page = str;    }    // error_page    str = ini.GetValue("error_page");    if (str.size() != 0) {        g_http_service.error_page = str;    }    // index_of    str = ini.GetValue("index_of");    if (str.size() != 0) {        g_http_service.index_of = str;    }    // ssl    str = ini.GetValue("ssl");    if (getboolean(str.c_str())) {        g_http_server.ssl = 1;        std::string crt_file = ini.GetValue("ssl_certificate");        std::string key_file = ini.GetValue("ssl_privatekey");        std::string ca_file = ini.GetValue("ssl_ca_certificate");        hssl_ctx_init_param_t param;        memset(¶m, 0, sizeof(param));        param.crt_file = crt_file.c_str();        param.key_file = key_file.c_str();        param.ca_file = ca_file.c_str();        if (hssl_ctx_init(¶m) == NULL) {            hloge("SSL certificate verify failed!");            exit(0);        }        else {            hlogi("SSL certificate verify ok!");        }    }    hlogi("parse_confile('%s') OK", confile);    return 0;}static void on_reload(void* userdata) {    hlogi("reload confile [%s]", g_main_ctx.confile);    parse_confile(g_main_ctx.confile);}int main(int argc, char** argv) {    // g_main_ctx    main_ctx_init(argc, argv);    //int ret = parse_opt(argc, argv, options);    int ret = parse_opt_long(argc, argv, long_options, ARRAY_SIZE(long_options));    if (ret != 0) {        print_help();        exit(ret);    }    /*    printf("---------------arg------------------------------\n");    printf("%s\n", g_main_ctx.cmdline);    for (auto& pair : g_main_ctx.arg_kv) {        printf("%s=%s\n", pair.first.c_str(), pair.second.c_str());    }    for (auto& item : g_main_ctx.arg_list) {        printf("%s\n", item.c_str());    }    printf("================================================\n");    */    /*    printf("---------------env------------------------------\n");    for (auto& pair : g_main_ctx.env_kv) {        printf("%s=%s\n", pair.first.c_str(), pair.second.c_str());    }    printf("================================================\n");    */    // help    if (get_arg("h")) {        print_help();        exit(0);    }    // version    if (get_arg("v")) {        print_version();        exit(0);    }    // parse_confile    const char* confile = get_arg("c");    if (confile) {        strncpy(g_main_ctx.confile, confile, sizeof(g_main_ctx.confile));    }    parse_confile(g_main_ctx.confile);    // test    if (get_arg("t")) {        printf("Test confile [%s] OK!\n", g_main_ctx.confile);        exit(0);    }    // signal    signal_init(on_reload);    const char* signal = get_arg("s");    if (signal) {        signal_handle(signal);    }#ifdef OS_UNIX    // daemon    if (get_arg("d")) {        // nochdir, noclose        int ret = daemon(1, 1);        if (ret != 0) {            printf("daemon error: %d\n", ret);            exit(-10);        }    }#endif    // pidfile    create_pidfile();    // http_server    Router::Register(g_http_service);    g_http_server.service = &g_http_service;    ret = http_server_run(&g_http_server);    return ret;}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
4.1.3 router.h 網址路由
#ifndef HV_HTTPD_ROUTER_H#define HV_HTTPD_ROUTER_H#include "HttpService.h"#include "handler.h"class Router {public:    static void Register(HttpService& http) {        // preprocessor => Handler => postprocessor        http.preprocessor = Handler::preprocessor;        http.postprocessor = Handler::postprocessor;        // curl -v http://ip:port/ping        http.GET("/ping", [](HttpRequest* req, HttpResponse* resp) {            resp->body = "pong";            return 200;        });        // curl -v http://ip:port/echo -d "hello,world!"        http.POST("/echo", [](HttpRequest* req, HttpResponse* resp) {            resp->content_type = req->content_type;            resp->body = req->body;            return 200;        });        // curl -v http://ip:port/sleep?t=3        http.GET("/sleep", Handler::sleep);        // curl -v http://ip:port/query?page_no=1\&page_size=10        http.GET("/query", Handler::query);        // Content-Type: application/x-www-form-urlencoded        // curl -v http://ip:port/kv -H "content-type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'        http.POST("/kv", Handler::kv);        // Content-Type: application/json        // curl -v http://ip:port/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'        http.POST("/json", Handler::json);        // Content-Type: multipart/form-data        // bin/curl -v localhost:8080/form -F "user=admin pswd=123456"        http.POST("/form", Handler::form);        // curl -v http://ip:port/test -H "Content-Type:application/x-www-form-urlencoded" -d 'bool=1&int=123&float=3.14&string=hello'        // curl -v http://ip:port/test -H "Content-Type:application/json" -d '{"bool":true,"int":123,"float":3.14,"string":"hello"}'        // bin/curl -v http://ip:port/test -F 'bool=1 int=123 float=3.14 string=hello'        http.POST("/test", Handler::test);        // Content-Type: application/grpc        // bin/curl -v --http2 http://ip:port/grpc -H "content-type:application/grpc" -d 'protobuf'        http.POST("/grpc", Handler::grpc);        // RESTful API: /group/:group_name/user/:user_id        // curl -v -X DELETE http://ip:port/group/test/user/123        http.Delete("/group/:group_name/user/:user_id", Handler::restful);        // bin/curl -v localhost:8080/upload -F "file=@LICENSE"        http.POST("/upload", Handler::upload);        // curl -v http://ip:port/login -H "Content-Type:application/json" -d '{"username":"admin","password":"123456"}'        http.POST("/login", Handler::login);    }};#endif // HV_HTTPD_ROUTER_H
2. 編譯命令4.2.1 庫編譯
make libhvsudo make install
4.2.2 從examples裡提取httpd的編譯指令碼

Makefile裡有詳細的examples編譯指令碼,這裡看一個 httpd 編譯相關配置:

include config.mkinclude Makefile.varsMAKEF=$(MAKE) -f Makefile.inALL_SRCDIRS=. base utils event protocol http http/client http/server consul examplesLIBHV_SRCDIRS = . base utils eventLIBHV_HEADERS = hv.h hconfig.h hexport.hLIBHV_HEADERS += $(BASE_HEADERS) $(UTILS_HEADERS) $(EVENT_HEADERS)ifeq ($(WITH_PROTOCOL), yes)LIBHV_HEADERS += $(PROTOCOL_HEADERS)LIBHV_SRCDIRS += protocolendififeq ($(WITH_HTTP), yes)LIBHV_HEADERS += $(HTTP_HEADERS)LIBHV_SRCDIRS += httpifeq ($(WITH_HTTP_SERVER), yes)LIBHV_HEADERS += $(HTTP_SERVER_HEADERS)LIBHV_SRCDIRS += http/serverendififeq ($(WITH_HTTP_CLIENT), yes)LIBHV_HEADERS += $(HTTP_CLIENT_HEADERS)LIBHV_SRCDIRS += http/clientifeq ($(WITH_CONSUL), yes)LIBHV_HEADERS += $(CONSUL_HEADERS)LIBHV_SRCDIRS += consulendifendifendifclean:	$(MAKEF) clean SRCDIRS="$(ALL_SRCDIRS)"	$(RM) include/hvprepare:	$(MKDIR) bininstall:	$(MKDIR) $(INSTALL_INCDIR)	$(CP) include/hv/* $(INSTALL_INCDIR)httpd: prepare	$(RM) examples/httpd/*.o	$(MAKEF) TARGET=$@ SRCDIRS=". base utils event http http/server examples/httpd".PHONY: clean prepare libhv install examples httpd
4.2.3 編譯命令
make httpd
4.2.4 執行程式
bin/httpd -d# 或bin/httpd -c etc/httpd.conf -s restart -d

引數說明:

-h|--help 列印幫助-v|--version 版本資訊-c|--confile <confile> 指定配置檔案-t|--test 測試配置檔案-s|--signal <signal> 訊號 =[start,stop,restart,status,reload]-d|--daemon 後臺常駐程式-p|--port <port> 指定埠

9
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 開發懵逼了!誤用一個雙引號,生產資料全變0