精华内容
下载资源
问答
  • libhv FAQ

    千次阅读 2020-07-30 23:15:19
    libhv是一个跨平台的类似libevent、libev、libuv的异步IO事件循环库,但提供了更加简单的API接口和更加丰富的协议(包括http、ftp、smtp、dns、icmp等)。 libhv已广泛实用在公司的IOT平台、http API服务之中,正确...

    libhv是一个类似于libevent、libev、libuv的跨平台网络库,提供了更简单的接口和更丰富的协议。

    项目地址:https://github.com/ithewei/libhv.git
    码云镜像:https://gitee.com/ithewei/libhv.git
    QQ技术交流群:739352073
    libhv博客专栏:https://hewei.blog.csdn.net/category_9866493.html
    libhv源码分析:https://blog.csdn.net/qu1993/category_10637982.html

    Q:libhv和libevent、libev、libuv有什么不同

    A:

    • libevent最为古老、有历史包袱,bufferevent虽为精妙,却也难以上手;
    • libev可以说是libevent的简化版,代码极为精简,但宏定义用的过多,代码可读性不强,且在Windows上实现不佳;
    • libuv是nodejs的c底层库,最先也是由libevent+对Windows IOCP支持,后来才改写自成一体,同时实现了管道、文件的异步读写,很强大,但结构体比较多,封装比较深;
    • libhv本身是参考了libevent、libev、libuv的实现思路,它们的核心都是事件循环(即在一个事件循环中处理IO、定时器等事件),但提供的接口最为精简,API接近原生系统调用,最容易上手;
    • 具体这几个库的写法比较见https://github.com/ithewei/libhv/tree/master/echo-servers
    • 当然这几个库的性能是接近的,都将非阻塞IO多路复用用到了极致
    • 更详细介绍见国产开源库libhv为何能被awesome-c和awesome-cpp收录

    Q:libhv的定位

    A:精妙小巧跨平台,简单实用易上手

    • base封装了很多跨平台的代码,如hatomic原子操作、hthread线程、hmutex线程同步,当然这都是基于configure/cmake自动生成的hconfig.hhplatform.h两个头文件中提供的平台宏、编译器宏等实现的;
    • event模块则实现了事件循环(包括IO、timer、idle),不同的平台有不同的实现,如Linux使用epollWindows使用IOCPMac使用kqueueSolaris使用evport,感兴趣的可以读一读event下的源码;
    • http模块则基于event模块实现了本世纪最为通用的应用层协议http协议,包括http服务端和客户端,libhv中examples下提供的httpd,性能可媲美nginx服务;
    • 不妨勇敢的说,libhv是c++编写HTTP API服务端/客户端最简单的库,没有之一

    Q:libhv的发展规划

    A:基于event模块实现更多的常见应用层协议,如MQTTrediskafkamysql等;

    Q:libhv性能如何

    A:上面说了,libhv、libevent、libev、libuv几个库是性能是接近的,它们都将非阻塞IO多路复用用到了极致,可使用echo-servers下提供的benchmark.sh简单做个压力测试,在我个人开发机上QPS都在28K左右,不存在量级差距;
    此外libhv中提供的httpd QPS甚至略微超过nginx;

    Q:libhv稳定性如何,是否商用

    A:libhv自2018年5月创建,至今已有两年多迭代,400+提交,广泛用于公司IoT和HTTP API服务中,此外QQ群里也是有不少水友成功用在各种项目中,反馈很好;
    请放心使用,开源且保证长期维护,QQ群里也有很多大神积极解答。

    Q:libhv如何入门

    A:

    • 建议先从运行项目根目录下getting_started.sh脚本开始, 你会被libhv的httpd所展示的便利性所吸引;
    • unittestexamples下的示例代码;
    • 源码阅读推荐路线base->event->http

    Q:libhv如何使用

    A:libhv可通过Makefilecmake编译出动态库和静态库,make install后包含相关头文件(base模块下头文件比较分散,可直接#include "hv.h")和链接库文件即可使用;当然libhv模块划分清晰,低耦合,你也可以直接把源文件拿到自己项目中去编译,如日志功能hlog.hhlog.c就可以直接拿去用。

    Q:libhv在Windows下如何编译

    A:Windows下编译libhv请先使用cmake生成VS工程。
    附VS各版本下载地址VS2008 ~ VS2019下载地址
    cmake官网下载过慢的可以到gitee下载cmake releasehttps://gitee.com/ithewei/cmake-release
    cmake不会使用的请自行百度

    Q:Windows下编译不过

    A:Windows下VS编译最低要求VS2015(包括VS2015)版本,这是因为http模块中使用了一个modern c++ JSON解析库nlohmann::json,该json库使用方法见https://github.com/nlohmann/json

    如果实在想使用vs低版本编译的,可以在cmake时关闭使用了c++11的模块WITH_EVPPWITH_HTTP,只编译base、event等c模块。

    Q:Windows下链接不过

    A: Windows下cmake生成vs工程,打开hv.sln编译后会生成头文件include/hv、静态库lib/hv_static.lib和动态库lib/hv.dll,所以有动态库和静态库两种链库方式:

    1、动态导入库hv.lib + 动态库hv.dll

    方案一:工程-> 属性 -> Linker -> Input -> Addtional Dependencies 加 hv.lib
    方案二:代码里添加#pragma comment(lib, "hv.lib")

    2、静态库声明宏HV_STATICLIB + 静态库hv_static.lib

    • 工程–>属性–>c/c++–>预处理器–>预处理器定义中添加HV_STATICLIB预编译宏,以屏蔽hexport.h头文件中动态库导入宏#define HV_EXPORT __declspec(dllimport)
      如使用curl静态库类似加CURL_STATICLIB预编译宏
    • 工程-> 属性 -> Linker -> Input -> Addtional Dependencies 加 hv_static.lib
      代码里添加#pragma comment(lib, "hv_static.lib")

    Q:如何开启SSL/TLS、https、wss功能

    A:libhv中集成了openssl来支持SSL/TLS加密通信,通过打开config.mkCMakeList.txtWITH_OPENSSL选项,编译即可。

    Makefile方式:

    ./configure --with-openssl
    make clean && make && sudo make install
    

    cmake方式:

    mkdir build
    cd build
    cmake .. -DWITH_OPENSSL=ON
    cmake --build .
    sudo cmake --install .
    

    测试https:

    bin/httpd -s restart -d
    bin/curl -v http://localhost:8080
    bin/curl -v https://localhost:8443
    # curl -v https://127.0.0.1:8443 --insecure
    

    https代码示例可以参考examples/http_server_test.cppTEST_HTTPS相关内容
    wss代码示例可以参考examples/websocket_server_test.cppTEST_WSS相关内容

    当然你也可以用nginxhttps代理。

    Q:Windows下如何集成openssl

    A:Windows下请自行下载或编译openssl,将openssl头文件include和库文件lib放到libhv可搜索路径(如libhv根目录下includelib)。

    giteeWindows openssl已编译好的https://gitee.com/ithewei/openssl-release.git
    (需将libssl.dll.a改名为ssl.liblibcrypto.dll.a改名为crypto.lib

    Q:https/wss连接失败排除步骤

    1、确认是否已开启with-openssl

    确认方法如下:

    • linux下可使用ldd libhv.so,查看动态库依赖项中是否有libssl.so、libcrypto.so
    • windows下使用命令行工具dumpbin /DEPENDENTS libhv.dll或者图形界面工具dependency查看
    • 代码里可打印hssl_backend(),如打印openssl则表示使用了openssl

    2、连接失败后,日志里查看是否有ssl handshake failed失败的字样,如有表示开启了SSL,但是握手失败,具体原因可能是服务端开启了验证客户端证书,此时需要调用hssl_ctx_init输入有效的证书。

    接口定义:

    typedef struct {
        const char* crt_file;
        const char* key_file;
        const char* ca_file;
        short       verify_peer;
        short       endpoint; // 0: server 1: client
    } hssl_ctx_init_param_t;
    
    HV_EXPORT hssl_ctx_t hssl_ctx_init(hssl_ctx_init_param_t* param);
    

    调用示例:

        hssl_ctx_init_param_t param;
        memset(&param, 0, sizeof(param));
        param.crt_file = "cert/server.crt";
        param.key_file = "cert/server.key";
        if (hssl_ctx_init(&param) == NULL) {
            fprintf(stderr, "SSL certificate verify failed!\n");
            return -20;
        }
    

    Q:httpd如何上传、下载文件

    A:上传文件推荐使用Content-Type: multipart/form-data,服务端示例见examples/httpd/handler.h中的/upload ,客户端测试可使用curl命令bin/curl -v localhost:8080/upload -F "file=@LICENSE"
    (传输文件一般使用form-data格式,使用json传输二进制文件需要base64编码成可打印文本,3字节变4字节增加了负载;对于大文件分片传输可自行百度HTTP头Range的使用)
    下载文件就不用多说了,web服务器本身就是文件服务器,httpd中已实现了web服务器,类似于curl http://ip:port/index.html,下载文件curl http://ip:port/path/to/filename响应中的body即是文件内容

    Q:c++已经跨平台,base模块为何要封装跨平台操作

    A:
    1、c++标准库提取的是所有操作系统的共性,所以它甚至不能像其它语言(没有操作系统包袱,只需要满足主流操作系统)那样提供通用的时间日期操作,也没有提供差异化的锁(自旋锁、读写锁),你可以发现java中锁的类型一大堆,而c++只有一个mutex;至于没有提供标准网络库,更是c++一直被诟病之处。
    2、event模块是纯c实现的,libevent、libuv也是如此,底层库使用c++性能有损、库大小、复杂度也会增加,并不会带来编码上的简化。如果只把libhv当作libevent来使用,关闭WITH_HTTP选项,是可以做到不依赖stdc++的。event模块本身也是封装了各种操作系统的IO多路复用机制(如linux的epollbsd的kqueue通用的select、poll等),提供出了统一的非阻塞IO接口。
    3、http模块使用c++的考量,是为了接口使用上的便利性(HttpRequestHttpResponse中使用了map、string来表示headers、bodyjson、form、kv来存储各种Content-Type解析后的结构化数据,Get、Set模板函数屏蔽了int、float、string之间的类型转化),你如果使用过libeventevhttp就会发现,c写这些会非常痛苦。
    4、没有任何贬低或者褒奖c、c++,归根结底它们只是有各自特色的编程语言,只是你实现业务的工具,避其糟粕、用其精华、为你所有,才是其价值。
    如果你是写数据库的CRUD应用,提供http api服务,我也并不推荐使用libhv,使用golang、python、ruby它不香吗?c++ http库使用场景可能就是需要将c接口SDK的算法功能以http api服务的方式提供出去。

    Q:libhv提倡的编程范式?

    A:
    c/c++本身是一种支持多编程范式的语言,简单的函数式编程,流行的OOP面向对象编程、还有c++的GP泛型编程,也就是模板编程。语言没有谁好谁坏,只有其适用场景,编程范式亦是如此。c with class我认为恰恰是c++最精华之处。

    所以event模块中将IO、timer、idle统一抽象成事件,方便放入事件队列中统一调度,也是一种OOP的思想,而http模块中也不是全是class,也有很多函数式,强行封装成类,反而显得别扭。

    而模板编程的核心是使静态类型语言具有动态类型的泛化,STL就是泛型编程的典范,其提供的容器如vector、list、deque、map、set、算法如max、min、sort、count、find、search、transform,应该是每个c++ coder应该熟练掌握的,即使如此,它的源码可读性还是很低,所以没有一定的功底和必要性,不推荐烂用模板编程。

    展开全文
  • libhv Like libevent, libev, and libuv, libhv provides event-loop with non-blocking IO and timer, but simpler api and richer protocols. ✨ Features Cross-platform (Linux, Windows, Mac, Solaris) ...
  • libhv每日一学

    万次阅读 2020-01-09 11:37:10
    文章目录libhv简介libhv应用程序框架libhv事件循环使用入门libhv日志模块介绍libhv如何实现跨平台的libhv中的宏艺术c语言如何实现c++的继承libevent、libev、libuv、libhv、boost.asio、poco、muduo七种echo-server...

    libhv简介

    libhv是一个类似于libevent、libev、libuv的跨平台网络库,提供了更简单的接口和更丰富的协议。

    项目地址:https://github.com/ithewei/libhv.git
    码云镜像:https://gitee.com/ithewei/libhv.git
    QQ技术交流群:739352073
    libhv博客专栏:https://hewei.blog.csdn.net/category_9866493.html
    libhv源码分析:https://blog.csdn.net/qu1993/category_10637982.html

    注:libhv每日一学博文为QQ群里的libhv每日一学技术分享整理所得,方便新老朋友查阅学习,该博文每隔几日会同步更新一次。

    http模块(包含http、https、http2、grpc、RESTful API)

    http编译测试,包含web serviceindexof serviceapi service (支持RESTful API
    HTTP API ContentType支持application/jsonapplication/x-www-form-urlencodedmultipart/form-data的构造和解析

    git clone https://github.com/ithewei/libhv.git
    cd libhv
    make httpd curl
    
    bin/httpd -h
    bin/httpd -d
    #bin/httpd -c etc/httpd.conf -s restart -d
    ps aux | grep httpd
    
    # http web service
    bin/curl -v localhost:8080
    
    # http indexof service
    bin/curl -v localhost:8080/downloads/
    
    # http api service
    bin/curl -v localhost:8080/ping
    bin/curl -v localhost:8080/echo -d "hello,world!"
    bin/curl -v localhost:8080/query?page_no=1\&page_size=10
    bin/curl -v localhost:8080/kv   -H "Content-Type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
    bin/curl -v localhost:8080/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
    bin/curl -v localhost:8080/form -F "user=admin pswd=123456"
    bin/curl -v localhost:8080/upload -F "file=@LICENSE"
    
    bin/curl -v localhost:8080/test -H "Content-Type:application/x-www-form-urlencoded" -d 'bool=1&int=123&float=3.14&string=hello'
    bin/curl -v localhost:8080/test -H "Content-Type:application/json" -d '{"bool":true,"int":123,"float":3.14,"string":"hello"}'
    bin/curl -v localhost:8080/test -F 'bool=1 int=123 float=3.14 string=hello'
    # RESTful API: /group/:group_name/user/:user_id
    bin/curl -v -X DELETE localhost:8080/group/test/user/123
    
    # webbench (linux only)
    make webbench
    bin/webbench -c 2 -t 60 localhost:8080
    

    libhv提供的httpd性能可媲美nginx
    libhv-vs-nginx
    indexof service目录服务效果图:
    indexof
    https编译测试,集成了openssl库(修改config.mkWITH_OPENSSL=yes
    https
    http2编译测试,集成了nghttp2
    nghttp2
    注:以下是模拟HTTP1的打印结果,HTTP2是二进制协议,采用了HPACK头部压缩和帧的概念
    http2
    通过libhv库编写http API是如此简单,支持RESTful API,并且可扩展成多进程/多线程模型
    下面贴出最基础的body用法,其它ContentType用法见examples/httpd/

    #include "HttpServer.h"
    
    int main() {
        HttpService service;
        service.base_url = "/v1/api";
        service.POST("/echo", [](HttpRequest* req, HttpResponse* res) {
            res->body = req->body;
            return 200;
        });
    
        http_server_t server;
        server.port = 8080;
        server.service = &service;
        http_server_run(&server);
        return 0;
    }
    

    日志模块

    hlog

    libhv应用程序框架

    libhv提供了命令行解析、INI配置文件解析、日志文件、pid文件、信号处理等创建一个应用程序的常用模块

    参考examples/hmain_test.cpp,讲解这些模块使用方法

    测试示例:

    make hmain_test
    bin/hmain_test -h
    bin/hmain_test -v
    bin/hmain_test -t
    bin/hmain_test -d
    ps aux | grep hmain_test
    bin/hmain_test -s status
    bin/hmain_test -s stop
    ps aux | grep hmain_test
    bin/hmain_test -s start -d
    ps aux | grep hmain_test
    bin/hmain_test -s restart -d
    ps aux | grep hmain_test
    

    流程图:
    main.cpp

    libhv事件循环使用入门

    参考examples/loop.cexamples/timer.cexamples/tcp.cexamples/udp.cexamples/nc.c

    make tcp udp nc
    bin/tcp 1111
    bin/nc 127.0.0.1 1111
    
    bin/udp 2222
    bin/nc -u 127.0.0.1 2222
    
    make hloop_test
    bin/hloop_test
    bin/nc 127.0.0.1 10514
    
    // TCP echo server
    #include "hloop.h"
    
    void on_close(hio_t* io) {
    }
    
    void on_recv(hio_t* io, void* buf, int readbytes) {
        hio_write(io, buf, readbytes);
    }
    
    void on_accept(hio_t* io) {
        hio_setcb_close(io, on_close);
        hio_setcb_read(io, on_recv);
        hio_read(io);
    }
    
    int main(int argc, char** argv) {
        if (argc < 2) {
            printf("Usage: cmd port\n");
            return -10;
        }
        int port = atoi(argv[1]);
    
        hloop_t* loop = hloop_new(0);
        hio_t* listenio = hloop_create_tcp_server(loop, "0.0.0.0", port, on_accept);
        if (listenio == NULL) {
            return -20;
        }
        hloop_run(loop);
        hloop_free(&loop);
        return 0;
    }
    

    流程图:
    hloop

    libevent、libev、libuv、libhv、boost.asio、poco、muduo七种echo-server实现对比

    https://github.com/ithewei/libhv/tree/master/echo-servers中包含libevent、libev、libuv、libhv、boost.asio、poco、muduo七种echo-server实现,感兴趣的可以看看

    编译测试步骤见README.md 中的echo-servers/benchmark

    # ubuntu16.04
    sudo apt install libevent-dev libev-dev libuv1-dev libboost-dev libboost-system-dev libasio-dev libpoco-dev
    # muduo install => https://github.com/chenshuo/muduo.git
    cd echo-servers
    ./build.sh
    ./benchmark.sh
    

    压力测试结果图:
    echo-servers
    注:客户端和服务端位于同一台电脑,有一定随机性,仅供参考,总的来说,这几个库性能接近,各有千秋吧

    libhv如何实现跨平台的

    主要靠两个文件:
    1、./configure生成的hconfig.h
    configure脚本中检测头文件、函数是否存在定义相应宏(如HAVE_PTHREAD_HHAVE_GETTIMEOFDAY)
    2、base/hplatform.h
    操作系统宏:(如_WIN32__linux__等)
    编译器宏:(如__GNUC____clang___MSC_VER等)
    CPU体系结构宏:(如__i386____x86_64____arm____aarch64__等)
    编程语言宏:__cplusplus

    以获取当前本地日期时间为例,见base/htime.c
    datetime_now

    libhv中的宏艺术

    C语言宏基础知识

    宏是C/C++语言的一大特色,它将一个标识符定义为一个字符串,在预处理阶段源程序中的该标识符均以指定的字符串来代替,使用宏可以使代码更加简洁和增强可读性。

    #define <宏名> (<参数表>) <宏体>
    #undef <宏名>
    
    #ifdef <宏名>
        ...
    #else
        ...
    #endif
    
    //define中的三个特殊符号:#,##,#@
    #define STRCAT(x,y) x##y //连接x和y成一个字符串
    #define TOCHAR(x) #@x  //给x加上单引号
    #define TOSTR(x) #x //给x加上双引号
    

    base/herr.hbase/herr.c中对错误码定义为例:

    #define FOREACH_ERR_COMMON(F)   \
        F(0,    OK,             "OK")               \
        F(1000, UNKNOWN,        "Unknown error")    \
        \
        F(1001, NULL_PARAM,     "Null parameter")   \
        F(1002, NULL_POINTER,   "Null pointer")     \
        F(1003, NULL_DATA,      "Null data")        \
        F(1004, NULL_HANDLE,    "Null handle")      \
        \
        F(1011, INVALID_PARAM,      "Invalid parameter")\
        F(1012, INVALID_POINTER,    "Invalid pointer")  \
        F(1013, INVALID_DATA,       "Invalid data")     \
        F(1014, INVALID_HANDLE,     "Invalid handle")   \
        F(1015, INVALID_JSON,       "Invalid json")     \
        F(1016, INVALID_XML,        "Invalid xml")      \
        F(1017, INVALID_FMT,        "Invalid format")   \
        F(1018, INVALID_PROTOCOL,   "Invalid protocol") \
        F(1019, INVALID_PACKAGE,    "Invalid package")  \
    
    #define FOREACH_ERR(F)      \
        FOREACH_ERR_COMMON(F)   \
        FOREACH_ERR_FUNC(F)     \
        FOREACH_ERR_SERVICE(F)  \
        FOREACH_ERR_GRPC(F)     \
    
    #undef ERR_OK // prevent conflict
    enum {
    #define F(errcode, name, errmsg) ERR_##name = errcode,
        FOREACH_ERR(F)
    #undef  F
    };
    
    // errcode => errmsg
    const char* hv_strerror(int err) {
        if (err > 0 && err <= SYS_NERR) {
            return strerror(err);
        }
    
        switch (err) {
    #define F(errcode, name, errmsg) \
        case errcode: return errmsg;
        FOREACH_ERR(F)
    #undef  F
        default:
            return "Undefined error";
        }
    }
    

    hv_strerror中宏替换后实际上是很多个case errcode: return errmsg;,添加一个错误码定义只需在头文件见中添加即可,无需改动源文件,代码更简洁,可扩展性更好

    golang defer 宏实现

    defer在作用域释放时做一些清理工作,可避免return前漏做,或者到处是调用清理函数,或者乱用goto导致的可读性差 等问题
    base/hscope.h,此头文件中还定义了很多利用作用域和RAII机制释放资源的模板类型,感兴趣的可以看看,用在自己项目中

    // same as golang defer
    class Defer {
    public:
        Defer(Function&& fn) : _fn(std::move(fn)) {}
        ~Defer() { if(_fn) _fn();}
    private:
        Function _fn;
    };
    #define defer(code) Defer STRINGCAT(_defer_, __LINE__)([&](){code});
    

    测试代码见unittest/defer_test.cpp

    java synchronized 一行宏实现

    #define synchronized(lock) for (std::lock_guard<std::mutex> _lock_(lock), *p = &_lock_; p != NULL; p = NULL)
    

    测试代码见unittest/synchronized_test.cpp

    libhv多线程同步相关知识

    base/hthread.h base/hmutex.h unittest/hmutex_test.c
    hthread
    hmutex
    pthread和hmutex对应的宏就不贴出来了,请自行查阅base/hmutex.h
    编译运行单元测试

    make unittest
    bin/hmutex_test
    

    跨平台socket编程

    Windows网络编程和Unix网络编程区别:

    • 头文件和库文件不同;
    • Windows下需要调用WSAStartup初始化;
    • 获取错误码方式不同,以及错误码不同,Windows错误码以WSA开头
    • Unix下没有closesocket;
    • 设置发送超时和接受超时参数类型要求不同;
    • 设置非阻塞方式不同;
    • 非阻塞connect返回值不同;
      hsocket
      SO_RCVTIMEO
      connect

    如何编写兼容IPv6的网络程序

    IPv6

    c语言如何实现c++的继承

    使用宏即可实现,原理如下
    inherit
    libhv中就应用了这种技巧,见event/hloop.hevent/hevent.h
    hevent_s
    htimer_s

    libhv事件循环逻辑

    int hloop_run(hloop_t* loop) {
        loop->status = HLOOP_STATUS_RUNNING;
        while (loop->status != HLOOP_STATUS_STOP) {
            if (loop->status == HLOOP_STATUS_PAUSE) {
                msleep(PAUSE_TIME);
                hloop_update_time(loop);
                continue;
            }
            ++loop->loop_cnt;
            if (loop->nactives == 0) break;
            hloop_process_events(loop);
            if (loop->flags & HLOOP_FLAG_RUN_ONCE) {
                break;
            }
        }
        loop->status = HLOOP_STATUS_STOP;
        loop->end_hrtime = gethrtime();
        if (loop->flags & HLOOP_FLAG_AUTO_FREE) {
            hloop_cleanup(loop);
            SAFE_FREE(loop);
        }
        return 0;
    }
    

    调用hloop_run后,我们就进入了libhv的事件循环,很简单,就是while循环中调用hloop_process_events处理各类事件
    hloop_process_events
    具体各类事件是如何处理的,感兴趣的可以研究源码

    IO与timer的完美结合

    设置连接超时

    // connect timeout => hclose_cb
    HV_EXPORT void hio_set_connect_timeout(hio_t* io, int timeout_ms DEFAULT(HIO_DEFAULT_CONNECT_TIMEOUT));
    

    设置keepalive超时

    一段时间内无数据收发,自动断开连接

    // keepalive timeout => hclose_cb
    HV_EXPORT void hio_set_keepalive_timeout(hio_t* io, int timeout_ms DEFAULT(HIO_DEFAULT_KEEPALIVE_TIMEOUT));
    

    设置心跳

    每隔一段时间触发发送心跳包回调

    /*
    void send_heartbeat(hio_t* io) {
        static char buf[] = "PING\r\n";
        hio_write(io, buf, 6);
    }
    hio_set_heartbeat(io, 3000, send_heartbeat);
    */
    typedef void (*hio_send_heartbeat_fn)(hio_t* io);
    // heartbeat interval => hio_send_heartbeat_fn
    HV_EXPORT void hio_set_heartbeat(hio_t* io, int interval_ms, hio_send_heartbeat_fn fn);
    

    libhv完美结合openssl

    libhv中使用openssl库实现TCP加密通信,这是几乎所有异步IO通信库都没有做的一点,而且libhv中开启SSL特别简单,仅需两个API
    1、初始化全局的SSL_CTX,见base/ssl_ctx.h

    int ssl_ctx_init(const char* crt_file, const char* key_file, const char* ca_file);
    

    2、启用SSL,见event/hloop.h

    int  hio_enable_ssl(hio_t* io);
    

    libhv中的https即是最好的例子:

    sudo apt-get install openssl libssl-dev # ubuntu下安装openssl依赖
    make clean
    make WITH_OPENSSL=yes
    修改配置文件etc/httpd.conf => ssl = on
    bin/httpd -d
    bin/curl -v https://localhost:8080
    curl -v https://localhost:8080 --insecure
    

    master-worker 多进程|多线程模型

    大多数库提供了ThreadPool线程池实现多线程模型
    nginx中则实现了master-workers多进程模型
    多线程的好处是数据共享以及跨平台性好(windows下实现多进程可不好做)
    多进程的好处是一个worker进程崩溃了,不影响其它worker进程正常工作

    libhv中提供了hthread_create创建线程,和spawn_proc(封装了fork)衍生进程
    并封装了master-workers模型,见utils/hmian.h中定义的master_workers_run函数
    给定一个工作函数(包含一个事件循环),可自由扩展成多进程 or 多线程 or 多进程|多线程 三种模式
    http_server_run即是调用了master_worker_run实现的,见效果图:
    httpd-master-workers
    简单说下master_workers_run的实现:
    在这里插入图片描述
    worker_proc
    最后,如果你看到了这里,觉得该项目不错的,请github
    star下,支持下国内开源,感谢!

    展开全文
  • libhv接口手册

    千次阅读 2020-01-14 17:28:46
    libhv是一个跨平台的类似libevent、libev、libuv的异步事件驱动库,但提供了更加接近原生的API接口和更加丰富的协议。 libhv已广泛实用在公司的IOT平台、http API服务之中,正确性、稳定性、可扩展性、性能都有保证...

    libhv接口手册

    libhv是一个类似于libevent、libev、libuv的跨平台网络库,提供了更简单的接口和更丰富的协议。

    项目地址:https://github.com/ithewei/libhv.git
    码云镜像:https://gitee.com/ithewei/libhv.git
    QQ技术交流群:739352073
    libhv博客专栏:https://hewei.blog.csdn.net/category_9866493.html
    libhv源码分析:https://blog.csdn.net/qu1993/category_10637982.html

    base基础设施

    base模块包含了一些c/c++基础设施,如常用宏定义、日期时间、字符串、文件、目录、进程、线程、套接字、日志、缓存等;

    hatomic.h:原子操作

    • 原子标记hatomic_flag_t
    hatomic_flag_t flag = HATOMIC_FLAG_INIT;
    hatomic_flag_test_and_set(&flag); // 原子测试并置位
    
    • 原子数hatomic_t
    hatomic_t cnt = HATOMIC_VAR_INIT(0);
    hatomic_inc(&cnt); // 原子自增
    hatomic_dec(&cnt); // 原子自减
    hatomic_add(&cnt, 3); // 原子加
    hatomic_sub(&cnt, 3); // 原子减
    

    测试代码见:https://github.com/ithewei/libhv/blob/master/unittest/hatomic_test.c

    hbase.h:基本接口

    • 安全alloc/free
      • safe_malloc
      • safe_realloc
      • safe_calloc
      • safe_zalloc
      • safe_free
      • HV_ALLOC
      • HV_ALLOC_SIZEOF
      • HV_FREE
      • HV_MEMCHECK:程序退出时打印alloc/free计数以判断程序是否有内存泄露
    • 字符串操作
      • strupper:字符串转大写
      • strlower:字符串转小写
      • strreverse:字符串翻转
      • strstartswith:判断字符串是否以xxx开头
      • strendswith:判断字符串是否以xxx结尾
      • strcontains:判断字符串是否包含xxx
      • safe_strncpy:安全strncpy
      • safe_strncat:安全strncat
      • strrchr_dot:查找最后一个点(通常用于提取文件后缀)
      • strrchr_dir:查找最后的路径(通常用于分离目录和文件)
      • hv_basename:获取文件名(利用了上面的strrchr_dir)
      • hv_suffixname:获取文件后缀(利用了上面的strrchr_dot)
      • getboolean1 y on yes true enable返回true(通常用于配置文件)
    • 获取运行时路径
      • get_executable_path:获取可执行文件绝对路径,例如/usr/local/bin/httpd
      • get_executable_dir:获取可执行文件所在目录,例如/usr/local/bin
      • get_executable_file:获取可执行文件名,例如httpd
      • get_run_dir:获取运行目录,例如/home/www/html
    • 其它
      • hv_mkdir_p:递归创建目录
      • hv_rmdir_p:递归删除目录

    hbuf.h:缓存

    • c普通buffer:hbuf_t
    typedef struct hbuf_s {
    	char* base; // 指针起始地址
    	size_t len; // buffer长度
    } hbuf_t;
    
    • 带偏移量buffer:offset_buf_t
    typedef struct offset_buf_s {
    	char* base; // 指针起始地址
    	size_t len; // buffer长度
    	size_t offset; // 偏移量
    } offset_buf_t;
    

    通常用于消费了一部分数据,需要记录下当前偏移。如用作socket写缓存,当一次写数据包过大时,一次无法发送全部数据,可以记录下当前发送的偏移量,当socket可写时再继续发送剩余的。

    • c++普通buffer:HBuf
    class HBuf : public hbuf_t { // 继承自c普通buffer
    public:
    	HBuf();
    	HBuf(void* data, size_t len);
    	HBuf(size_t cap) { resize(cap); }
    	virtual ~HBuf() { cleanup(); } // 析构时会自动是否内存
    
    	void*  data() { return base; }
    	size_t size() { return len; }
    	bool isNull() { return base == NULL || len == 0; }
    
    	void cleanup(); // free内存
    	void resize(size_t cap); // realloc
    	// 深拷贝(resize后memcpy)
    	void copy(void* data, size_t len);
    	void copy(hbuf_t* buf);
    };
    
    • 可变长buffer:HVLBuf
    class HVLBuf : public HBuf { // VL: Variable-Length可变长
    public:
    	HVLBuf();
    	HVLBuf(void* data, size_t len);
    	HVLBuf(size_t cap);
    	virtual ~HVLBuf() {}
    	
    	char* data() { return base + _offset; } // 返回当前偏移地址
    	size_t size() { return _size; } // 返回实际已存储数据量
    	void push_front(void* ptr, size_t len);
    	void push_back(void* ptr, size_t len);
    	void pop_front(void* ptr, size_t len);
    	void pop_back(void* ptr, size_t len);
    	void clear();
    	
    	void prepend(void* ptr, size_t len); // alias push_front
    	void append(void* ptr, size_t len); // alias push_back
    	void insert(void* ptr, size_t len); // alias push_back
    	void remove(size_t len); // alias pop_front
    	
    private:
    	size_t _offset; // 用于记录当前偏移量
    	size_t _size; // 用于记录实际存储数据量
    };
    

    HVLBuf可用作可增长数组、双端队列

    • 环形buffer:HRingBuf
    class HRingBuf : public HBuf {
    public:
    	HRingBuf();
    	HRingBuf(size_t cap);
    	virtual ~HRingBuf();
    
    	char* alloc(size_t len);
    	void free(size_t len);
    	void clear();
    	size_t size();
    	
    private:
    	size_t _head;
    	size_t _tail;
    	size_t _size;
    };
    

    HRingBuf初始化时分配一块大一点的内存,有序从中申请和释放,以避免频繁调用系统malloc/free(影响性能、造成内存碎片)。如用作音视频缓冲,解码线程不断申请内存生产帧数据,渲染线程不断消费帧数据并释放内存占用。

    hdef.h:常用宏定义

    #define ABS(n)  ((n) > 0 ? (n) : -(n)) // 绝对值
    #define NABS(n) ((n) < 0 ? (n) : -(n)) // 负绝对值
    
    #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) // 数组大小
    
    #define BITSET(p, n) (*(p) |= (1u << (n))) // 设置位
    #define BITCLR(p, n) (*(p) &= ~(1u << (n))) // 清除位
    #define BITGET(i, n) ((i) & (1u << (n))) // 获取位
    
    #define CR      '\r' // mac换行符
    #define LF      '\n' // unix换行符
    #define CRLF    "\r\n" // dos换行符
    
    #define IS_ALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) // 是否是字母
    #define IS_NUM(c)   ((c) >= '0' && (c) <= '9') // 是否是数字
    #define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) // 是否是字母或数字
    #define IS_CNTRL(c) ((c) >= 0 && (c) < 0x20) // 是否是控制符
    #define IS_GRAPH(c) ((c) >= 0x20 && (c) < 0x7F) // 是否是可打印字符
    #define IS_HEX(c) (IS_NUM(c) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) // 是否是16进制
    #define IS_LOWER(c) (((c) >= 'a' && (c) <= 'z')) // 是否是小写
    #define IS_UPPER(c) (((c) >= 'A' && (c) <= 'Z')) // 是否是大写
    
    #define LOWER(c)    ((c) | 0x20) // 字符转小写
    #define UPPER(c)    ((c) & ~0x20) // 字符转大写
    
    #define MAX(a, b) ((a) > (b) ? (a) : (b)) // 两者取大
    #define MIN(a, b) ((a) < (b) ? (a) : (b)) // 两者取小
    
    #define SAFE_FREE(p)    do {if (p) {free(p); (p) = NULL;}} while(0) // 安全free
    #define SAFE_DELETE(p)  do {if (p) {delete (p); (p) = NULL;}} while(0) // 安全delete
    #define SAFE_DELETE_ARRAY(p) do {if (p) {delete[] (p); (p) = NULL;}} while(0) // 安全delete[]
    

    以上列出一些最常用的,更多实用宏定义可自行浏览https://github.com/ithewei/libhv/blob/master/base/hdef.h

    hdir.h:ls实现

    hdir.h中目前只有listdir一个接口,使用简单。
    测试代码见:
    https://github.com/ithewei/libhv/blob/master/unittest/listdir_test.cpp
    对跨平台ls实现感兴趣的可以阅读源码:
    https://github.com/ithewei/libhv/blob/master/base/hdir.cpp
    unix平台使用 opendir -> readdir -> closedir
    windows平台使用FindFirstFile -> FindNextFile -> FindClose

    hendian.h:大小端

    • 大小端与主机序转化宏(h代表主机序,be代表大端序,le代表小端序,数字代表多少位整型)
      • htobe16
      • htobe32
      • htobe64
      • be16toh
      • be32toh
      • be64toh
      • htole16
      • htole32
      • htole64
      • le16toh
      • le32toh
      • le64toh
    • detect_endian:检测大小端
    • serialize<T>:序列化模板函数,如序列化浮点数serialize<float>
    • deserialize<T>:反序列化模板函数,如反序列化浮点数deserialize<float>

    herr.h:错误码

    herr.h中定义了一些错误码,用到了宏的映射技巧
    接口只有一个hv_strerror:根据错误码获取错误字符串

    hexport.h:导出宏

    • HV_EXPORT:接口导出宏
    • HV_DEPRECATED:声明废弃宏
    • HV_UNUSED:声明未使用宏
    • EXTERN_CBEGIN_EXTERN_CEND_EXTERN_C:c符号链接extern “C”相关宏
    • BEGIN_NAMESPACEEND_NAMESPACEUSING_NAMESPACE:c++命名空间相关宏

    hfile.h:文件类

    提供了简单好用的HFile

    class HFile {
    public:
    	HFile() { fp = NULL; }
    	~HFile() { close(); } // 析构时自动关闭文件
    	
    	int open(const char* filepath, const char* mode); // 打开文件(调用fopen)
    	void close(); // 关闭文件(调用fclose)
    	size_t read(void* ptr, size_t len); // 读文件(调用fread)
    	size_t write(const void* ptr, size_t len) // 写文件(调用fwrite)
    	size_t size(); // 返回文件大小(调用stat)
    	size_t readall(HBuf& buf); // 读取文件所有内容到buffer
    	size_t readall(std::string& str); // 读取文件所有内容到string
    	bool readline(std::string& str); // 逐行读取,成功返回true,失败返回false
    	
    public:
    	char  filepath[MAX_PATH];
    	FILE* fp;
    };
    

    hlog.h:日志

    • stdout_logger:标准输出日志
    • stderr_logger:标准错误日志
    • file_logger:文件日志
    • network_logger:网络日志(定义在https://github.com/ithewei/libhv/blob/master/event/nlog.h
    • logger_create:创建日志器
    • logger_destroy:销毁日志器
    • logger_set_handler:设置日志处理函数
    • logger_set_level:设置日志等级
    • logger_set_level_by_str:设置日志等级by字符串[VERBOSE,DEBUG,INFO,WARN,ERROR,FATAL,SILENT]
    • logger_set_max_bufsize:设置日志缓存大小
    • logger_enable_color:启用日志颜色
    • logger_print:日志打印
    • logger_set_file:设置日志文件
    • logger_set_max_filesize:设置日志文件大小
    • logger_set_max_filesize_by_str:设置日志文件大小by字符串,如16, 16M, 16MB都表示16M
    • logger_set_remain_days:设置日志文件保留天数
    • logger_enable_fsync:启用每次写日志文件立即刷新到磁盘(即每次都调用fsync,会增加IO耗时,影响性能)
    • logger_fsync:刷新缓存到磁盘(如对日志文件实时性有必要的,可使用定时器定时刷新到磁盘)
    • logger_get_cur_file:获取当前日志文件路径

    提供了默认的日志器hlog

    // macro hlog*
    #define hlog 							hv_default_logger()
    #define hlog_set_file(filepath)         logger_set_file(hlog, filepath)
    #define hlog_set_level(level)           logger_set_level(hlog, level)
    #define hlog_set_level_by_str(level)    logger_set_level_by_str(hlog, level)
    #define hlog_set_max_filesize(filesize) logger_set_max_filesize(hlog, filesize)
    #define hlog_set_max_filesize_by_str(filesize) logger_set_max_filesize_by_str(hlog, filesize)
    #define hlog_set_remain_days(days)      logger_set_remain_days(hlog, days)
    #define hlog_enable_fsync()             logger_enable_fsync(hlog, 1)
    #define hlog_disable_fsync()            logger_enable_fsync(hlog, 0)
    #define hlog_fsync()                    logger_fsync(hlog)
    #define hlog_get_cur_file()             logger_get_cur_file(hlog)
    

    提供了便利的日志宏hlogd, hlogi, hlogw, hloge, hlogf
    提供了更大众化的别名LOGD, LOGI, LOGW, LOGE, LOGF
    hlog跨平台、零依赖、多线程安全、使用简单、配置灵活、即使你不使用libhv,也可以直接将hlog.hhlog.c直接拷贝到你的项目中使用。

    hmain.h:命令行解析

    • main_ctx_init:main上下文初始化
    • parse_opt:解析命令行(类似于getopt
    • parse_opt_long:解析命令行(类似于getopt_long
    • get_arg:获取命令行参数值
    • get_env:获取环境变量
    • signal_init:信号初始化(内部初始化了SIGINTSIGCHLD子进程崩溃重启、和自定义的SIGNAL_TERMINATE退出所以进程、SIGNAL_RELOAD重新加载配置文件)
    • signal_handle:信号处理signal=[start,stop,restart,status,reload]
    • create_pidfile:创建pid文件
    • delete_pidfile:删除pid文件
    • getpid_from_pidfile:从pid文件中获取pid
    • setproctitle:设置进程标题(只在unix下生效)
    • master_workers_runmaster-workers模型,即多进程/多线程模型(参考了nginx

    hmain.h中提供一系列main入口有用的工具,如命令行解析信号处理创建pid文件、以及强大的master-workers模型
    测试代码见:https://github.com/ithewei/libhv/blob/master/examples/hmain_test.cpp
    测试步骤:

    make hmain_test # 编译hmain_test
    bin/hmain_test -h # -h打印帮助信息
    bin/hmain_test -v # -v打印版本信息
    bin/hmain_test -c etc/hmain_test.conf -t # -c设置配置文件 -t测试加载配置文件
    bin/hmain_test -d # -d后台运行
    cat logs/hmain_test.pid # 可以看到创建的pid文件
    ps aux | grep hmain_test # 可以看到master-workers模型效果
    bin/hmain_test -s status # -s表示信号处理,status表示查看进程状态
    bin/hmain_test -s stop # stop表示停止进程
    bin/hmain_test -s start # start表示启动进程
    bin/hmain_test -s restart -d # restart表示重启进程
    bin/hmain_test -s reload # reload表示重新加载配置文件(比如修改日志等级为DEBUG,不用重启进程就能打印DEBUG日志了)
    

    毫不夸张的说,搞懂了hmain_test.cpp,再也不用愁写不出规范的命令行程序了。

    hmath.h:数学函数

    • floor2e:2的指数倍向下取整,如floor2e(5) = 4
    • ceil2e:2的指数倍向上取整,如ceil2e(5) = 8

    hmutex.h:互斥锁

    互斥锁

    • hmutex_init
    • hmutex_destroy
    • hmutex_lock
    • hmutex_unlock

    自旋锁

    • hspinlock_init
    • hspinlock_destroy
    • hspinlock_lock
    • hspinlock_unlock

    读写锁

    • hrwlock_init
    • hrwlock_destroy
    • hrwlock_rdlock
    • hrwlock_rdunlock
    • hrwlock_wrlock
    • hrwlock_wrunlock

    定时锁

    • htimed_mutex_init
    • htimed_mutex_destroy
    • htimed_mutex_lock
    • htimed_mutex_unlock
    • htimed_mutex_lock_for

    条件变量

    • hcondvar_init
    • hcondvar_destroy
    • hcondvar_wait
    • hcondvar_wait_for
    • hcondvar_signal
    • hcondvar_broadcast

    信号量

    • hsem_init
    • hsem_destroy
    • hsem_wait
    • hsem_post
    • hsem_wait_for

    只执行一次

    • honce

    c++类封装

    • MutexLock:互斥锁
    • SpinLock:自旋锁
    • RWLock:读写锁
    • LockGuard:守护锁(构造时即lock,析构时即unlock,类似于std::lock_guard

    hthread.h跨平台,基于Windows APIpthread两套实现,命名类似pthread,无记忆负担,是不是很便利呢,妈妈再也不用担心没有c++,写不出跨平台的多线程同步代码了。

    鄙人强烈推荐读下hthread.h源码,对应付面试中各种同步锁大有好处、加深理解。
    测试代码见:https://github.com/ithewei/libhv/blob/master/unittest/hmutex_test.c

    此处附上一个彩蛋:java synchronized 一行宏实现

    #define synchronized(lock) for (std::lock_guard<std::mutex> _lock_(lock), *p = &_lock_; p != NULL; p = NULL)
    

    测试代码见:https://github.com/ithewei/libhv/blob/master/unittest/synchronized_test.cpp

    hplatform.h:平台相关宏

    • 操作系统宏:OS_WINOS_UNIXOS_LINUXOS_ANDROIDOS_DARWINOS_FREEBSDOS_OPENBSDOS_SOLARIS
    • 体系结构宏:ARCH_X86ARCH_X86_64ARCH_ARMARCH_ARM64
    • 编译器宏:COMPILER_MSVCCOMPILER_MINGWCOMPILER_GCCCOMPILER_CLANG
    • 字节序宏:BYTE_ORDERBIG_ENDIANLITTLE_ENDIAN

    hplatform.hhconfig.h./configure脚本生成的配置文件)是libhv跨平台的基石。

    hproc.h:进程

    • hproc_spawn:unix下使用多进程,windows使用多线程

    hscope.h:作用域

    • ScopeCleanup:作用域清理函数
    • ScopeFree:作用域free
    • ScopeDelete:作用域delete
    • ScopeDeleteArray:作用域delete[]
    • ScopeRelease:作用域release
    • ScopeLock:作用域锁

    hscope.h利用RAII机制,定义了一系列作用域模版类,方便做资源释放。

    此处附上一个彩蛋:golang defer 宏实现

    class Defer {
    public:
    	Defer(Function&& fn) : _fn(std::move(fn)) {}
    	~Defer() { if(_fn) _fn();}
    private:
    	Function _fn;
    };
    #define defer(code) Defer STRINGCAT(_defer_, __LINE__)([&](){code});
    

    defer测试代码见:https://github.com/ithewei/libhv/blob/master/unittest/defer_test.cpp

    hsocket.h:套接字

    • socket_errno:socket错误码
    • socket_strerror:根据socket错误码获取错误字符串
    • blocking:设置socket为阻塞
    • nonblocking:设置socket为非阻塞

    sockaddr_u:包裹了IPv4IPv6Unix Domian Socket

    typedef union {
    	struct sockaddr     sa;
    	struct sockaddr_in  sin; // IPv4
    	struct sockaddr_in6 sin6; // IPv6
    	struct sockaddr_un  sun; // Unix Domain Socket
    } sockaddr_u;
    

    下面是一些sockaddr_u的辅助函数:

    • Resolver:域名解析成sockaddr_u
    • sockaddr_ip:从sockaddr_u中获取ip地址
    • sockaddr_port:从sockaddr_u中获取端口
    • sockaddr_set_ipsockaddr_u设置ip地址
    • sockaddr_set_portsockaddr_u设置端口
    • sockaddr_set_ipportsockaddr_u设置ip地址和端口
    • sockaddr_set_pathsockaddr_u设置路径
    • sockaddr_lensockaddr_u根据sa_family,获取结构体长度
    • sockaddr_strsockaddr_u转化成可读字符串
    • sockaddr_printsockaddr_u打印可读字符串

    socket、bind、listen、connect封装

    • Bind:封装了socket -> setsockopt -> bind流程
    • Listen:封装了Bind -> listen流程
    • Connect:封装了Resolver -> socket -> nonblocking -> connect流程
    • ConnectNonblock:非阻塞connect,调用了Connect(host, port, 1)
    • ConnectTimeout:超时connect,封装了Connect(host, port, 1) -> select -> blocking

    Unix Domain Socket(以路径取代端口号)

    • BindUnix
    • ListenUnix
    • ConnectUnix
    • ConnectUnixNonblock
    • ConnectUnixTimeout

    setsockopt

    • tcp_nodelay:禁用Nagle算法,降低小包的响应延时
    • tcp_nopush:当包累计到一定大小后再发送,通常与sendfile配合使用,提高大数据的通信性能
    • tcp_keepalive:设置TCP保活
    • udp_broadcast:设置UDP广播
    • so_sndtimeo:设置发送超时
    • so_rcvtimeo:设置接收超时

    hsocket.hhsocket.c展示了跨平台socket编程的写法,适配了IPv4、IPv6、Unix Domain Socket,可以说是UNPUnix Network Programming:UNIX网络编程)的实践,推荐网络编程初学者阅读源码。

    hssl.h:SSL/TLS加密通信

    • hssl_ctx_init:SSL_CTX初始化
    • hssl_ctx_cleanup:SSL_CTX清理
    • hssl_ctx_instance:返回SSL_CTX实例
    • hssl_new:SSL创建
    • hssl_free:SSL释放
    • hssl_accept
    • hssl_connect
    • hssl_read
    • hssl_write
    • hssl_close

    hssl.h封装了SSL/TLS操作,目前使用openssl实现,编译时可选择打开WITH_OPENSSL选项。

    hstring.h:字符串

    • hv::to_string:T转字符串模板函数
    • hv::from_string:字符串转T模板函数
    • asprintf:格式化输出字符串,如asprintf("%d+%d=%d", 1, 2, 3)返回字符串“1+2=3”
    • split:分割字符串成字符列表,如split("1, 2, 3"),返回字符串列表["1", "2", "3"]
    • splitKV:分割KV字符串,如splitKV("user=admin&pswd=123456")返回map{"user": "admin", "pswd": "123456"}
    • trim:修剪字符串
    • trimL:修剪字符串左边
    • trimR:修剪字符串右边
    • trim_pairs:指定pairs,修剪字符串
    • replace:替换字符串

    文件路径

    • basename:类似shell命令basename
    • dirname:获取目录名,类似shell命令dirname
    • filename:获取文件名
    • suffixname:获取后缀名

    例如:

    std::string filepath = "/mnt/share/image/test.jpg";
    basename(filepath) = "test.jpg"
    dirname(filepath) = "/mnt/share/image"
    filename(filepath) = "test"
    suffixname(filepath) = "jpg"
    

    测试代码见:https://github.com/ithewei/libhv/blob/master/unittest/hstring_test.cpp

    hsysinfo.h:系统信息

    • get_ncpu:获取CPU逻辑核数
    • get_meminfo:获取内存信息

    hthread.h:线程

    • hv_getpid:获取进程id
    • hv_gettid:获取线程id
    • hthread_create:创建线程
    • hthread_join:加入线程(等待线程退出)

    c++提供了一个HThread线程封装类

    class HThread {
    public:
    	virtual int start(); // 开始
    	virtual int stop(); // 结束
    	virtual int pause(); // 暂停
    	virtual int resume(); // 继续
    	virtual void run(); // 可重载run自定义线程过程
    	virtual void doTask(); // 可重载doTask实现线程循环中的任务函数
    };
    

    hthread.h封装了跨平台的线程操作,在不使用c++ std::thread的情况下也能写出跨平台的创建线程程序了。

    hthreadpool.h:线程池

    class HThreadPool {
    public:
    	HThreadPool(int size = std::thread::hardware_concurrency()); // size为线程池中线程数量
    	int start(); // 开始
    	int stop(); // 停止
    	int pause(); // 暂停
    	int resume(); // 继续
    	int wait(); // 等待所有任务做完
    	// commit:提交任务
    	// return a future, calling future.get() will wait task done and return RetType.
    	// commit(fn, args...)
    	// commit(std::bind(&Class::mem_fn, &obj))
    	// commit(std::mem_fn(&Class::mem_fn, &obj))
    	template<class Fn, class... Args>
    	auto commit(Fn&& fn, Args&&... args) -> std::future<decltype(fn(args...))>
    };
    

    测试代码见:https://github.com/ithewei/libhv/blob/master/unittest/threadpool_test.cpp

    htime.h:时间日期

    htime.hhtime.c封装了跨平台的日期时间方法,接口如下:

    • gettick:返回tick毫秒数
    • gettimeofday_ms:返回gettimeofday毫秒数
    • gethrtime_us:返回高精度微秒数
    • datetime_now:返回日期时间
    • datetime_mktime:根据datetime_t返回时间戳
    • datetime_past:返回过去几天前的datetime_t
    • datetime_future:返回未来几天后的datetime_t
    • duration_fmt:时长格式化成字符串
    • datetime_fmt:datetime_t格式化成字符串
    • gmtime_fmt:GMT时间格式化成字符串
    • days_of_month:返回某月有多少天
    • month_atoi:月份字符串转整数months=["January", "February", "March", "April", "May", "June","July", "August", "September", "October", "November", "December"]
    • month_itoa:月份整数转字符串
    • weekday_atoi:星期字符串转整数weekdays=["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
    • weekday_itoa:星期整数转字符串
    • hv_compile_datetime:返回libhv库的编译日期时间
    • cron_next_timeout:计算cron下一次触发时间

    测试代码见:https://github.com/ithewei/libhv/blob/master/unittest/date_test.c

    hurl.h:URL相关

    • url_escape:URL转义(将URL中有歧义字符如/,转化成%02X16进制格式)
    • url_unescape:URL反转义

    hversion.h:版本

    • hv_version:返回libhv静态版本
    • hv_compile_version:返回libhv编译版本
    • version_atoi:版本字符串转整型(如1.2.3.4 => 0x01020304
    • version_itoa:版本整型转字符串(如0x01020304 => 1.2.3.4

    hv.h:总头文件

    base下的头文件比较分散,所以提供了一个hv.h的总头文件

    ifconfig.h:ifconfig实现

    • ifconfig:获取网络接口信息,类似shell下ifconfig命令

    测试代码见:https://github.com/ithewei/libhv/blob/master/unittest/ifconfig_test.cpp

    event事件循环

    event模块是libhv的事件循环模块,实现了类似libevent、libev、libuv非阻塞IO、定时器等功能。
    如果说base模块是libhv的基石,event模块就是libhv的灵魂,上层的应用层协议(如http、websocket、redis、mqtt)都将基于event模块搭建。

    hloop.h:事件循环

    • hloop_new:创建事件循环实例
    • hloop_free:释放事件循环实例
    • hloop_run:运行事件循环
    • hloop_stop:停止事件循环
    • hloop_pause:暂停事件循环
    • hloop_resume:继续事件循环
    • hloop_update_time:更新事件循环时间
    • hloop_now:返回事件循环里的当前时间(单位s)
    • hloop_now_ms:返回事件循环里的当前时间(单位ms)
    • hloop_now_us:返回事件循环里的当前时间(单位us)
      用户数据
    • hloop_set_userdata:设置用户数据
    • hloop_userdata:返回用户数据
      自定义事件
    • hloop_post_event:向事件循环投递自定义事件(此接口线程安全)
      事件基类
      hio_thtimer_thidle_t皆是hevent_t的子类,继承hevent_t数据成员以及函数成员
    • hevent_set_priority:设置事件优先级
    • hevent_set_userdata:设置事件用户数据
    • hevent_loop:返回当前loop
    • hevent_type:返回事件类型
    • hevent_id:返回事件id
    • hevent_priority:返回事件优先级
    • hevent_userdata:返回事件用户数据
      idle空闲事件
    • hidle_add:添加空闲事件回调
    • hidle_del:删除空闲事件
      timer定时器事件
    • htimer_add:添加定时器
    • htimer_add_period:添加period型定时器(类似于cron
    • htimer_del:删除定时器
    • htimer_reset:重置定时器
      IO事件
      low-level apis
    • hio_engine:返回底层IO多路复用引擎(select/poll/epoll/kqueue/iocp/evport
    • hio_get:根据sockfd返回一个hio_t实例指针
    • hio_add:添加IO读写事件
    • hio_del:删除IO读写事件
    • hio_fd:返回文件描述符
    • hio_error:返回错误码
    • hio_type:返回IO类型(STDIO/FILE/TCP/UDP/SSL等)
    • hio_localaddr:返回本端地址
    • hio_peeraddr:返回对端地址
    • hio_setcb_accept:设置accept回调
    • hio_setcb_connect:设置connect回调
    • hio_setcb_read:设置read回调
    • hio_setcb_write:设置write回调
    • hio_setcb_close:设置close回调
    • hio_enable_ssl:启用SSL/TLS
    • hio_set_readbuf:设置读缓存buffer(每一个事件循环占用一个线程,所以事件循环中有一个默认的读缓存,但是你可以通过此接口传入自己的buffer,避免后续再memcpy拷贝)
    • hio_set_connect_timeout:设置连接超时
    • hio_set_close_timeout:设置关闭超时(这里解释下为何非阻塞IO有关闭超时一说,因为可能存在写队列未发送完成的情况,所以需要延迟关闭套接字)
    • hio_set_keepalive_timeout:设置keepalive超时(一段时间无数据收发断开连接,http模块即用到了此接口,使用nc 127.0.0.1 8080连接后不发数据,75s后连接将被libhv httpd服务端强制断开)
    • hio_set_heartbeat:设置应用层心跳
    • hio_accept
    • hio_connect
    • hio_read
    • hio_write
    • hio_close

    high-level apis

    • hread
    • hwrite
    • hclose
      tcp
    • haccept
    • hconnect
    • hrecv
    • hsend
      udp
    • hio_set_type
    • hio_set_localaddr
    • hio_set_peeraddr
    • hrecvfrom
    • hsendto

    top-level apis

    • hloop_create_tcp_server:在事件循环中创建TCP服务端
    • hloop_create_tcp_client:在事件循环中创建TCP客户端
    • hloop_create_udp_server:在事件循环中创建UDP服务端
    • hloop_create_udp_client:在事件循环中创建UDP客户端

    hloop.h是事件循环对外头文件,如想使用libhv开发TCP/UDP自定义协议网络通信程序,建议通读此头文件。

    event模块封装了多种IO多路复用机制,感兴趣的可以阅读源码,你将对reactor模式select/poll/epoll有更深的理解。

    • linux下默认使用epoll
    • windows下使用pollIOCP尚不完善)
    • mac下使用kqueue
    • solaris下使用port

    测试代码见:

    测试步骤:
    examples下展示了用libhv实现echo、chat、proxy三种经典服务的写法,使用前event模块的必读。

    make examples
    
    # 回显
    bin/tcp_echo_server 1234
    bin/nc 127.0.0.1 1234
    
    # 群聊
    bin/tcp_chat_server 1234
    bin/nc 127.0.0.1 1234
    bin/nc 127.0.0.1 1234
    
    # 代理
    bin/httpd -s restart -d
    bin/tcp_proxy_server 1234 127.0.0.1:8080
    bin/curl -v 127.0.0.1:8080
    bin/curl -v 127.0.0.1:1234
    
    bin/udp_echo_server 1234
    bin/nc -u 127.0.0.1 1234
    

    nlog.h:网络日志

    make examples
    bin/hloop_test
    telnet 127.0.0.1 10514
    

    nmap.h:nmap实现

    make nmap
    sudo bin/nmap <ip>
    

    utils工具

    base64.h:base64编解码

    • base64_encode:base64编码
    • base64_decode:base64解码

    iniparser.h:ini解析

    class HV_EXPORT IniParser {
    public:
        int LoadFromFile(const char* filepath); // 从文件加载
        int LoadFromMem(const char* data); // 从内存加载
        int Unload(); // 卸载
        int Reload(); // 重新加载
    
        string DumpString(); // 转存为字符串
        int Save(); // 保存
        int SaveAs(const char* filepath); // 保存为
    
        string GetValue(const string& key, const string& section = ""); // 获取值
        void   SetValue(const string& key, const string& value, const string& section = ""); // 设置值
    
        // T = [bool, int, float]
        template<typename T>
        T Get(const string& key, const string& section = "", T defvalue = 0); // 获取值模板函数
    
        // T = [bool, int, float]
        template<typename T>
        void Set(const string& key, const T& value, const string& section = ""); // 设置值模板函数
    };
    

    json.hpp:json解析

    nlohmann::json是一个modern c++ json解析库,具体使用参考https://github.com/nlohmann/json
    (注:windows下该头文件需在VS2015以上版本才能顺利编译通过)

    md5.h:MD5数字摘要

    • MD5Init:MD5初始化MD5_CTX
    • MD5Update:MD5更新
    • MD5Final:MD5最后结果

    singleton.h:单例模式宏

    • DISABLE_COPY:禁止拷贝宏
    • SINGLETON_DECL:单例模式声明宏
    • SINGLETON_IMPL:单例模式实现宏

    http协议

    http模块的使用可参考这篇博客c++编写HTTP API服务端/客户端最简单的库,没有之一

    http模块是event模块目前仅有的得意门生,服务端接口参考了golang gin,客户端接口参考了python requests,使用c++开发,至于为何使用c++,如果你用过libeventevhttp,就知道c写上层逻辑是有多痛苦。
    我也不推荐使用libhv开发CRUD业务服务,使用golang、python、ruby它不香吗?c++开发http服务的场景可能就是需要将C接口的算法SDK以http api的方式提供出去。

    HttpMessage.h:http消息类

    • HttpMessage:http消息基类
    • HttpRequest:http请求类
    • HttpResponse:http响应类

    HttpMessage.h建议通读,才能更好的使用好libhv的http相关功能。

    HttpServer.h:http服务端

    • http_server_run

    测试代码见:https://github.com/ithewei/libhv/tree/master/examples/httpd
    测试步骤:

    ./getting_started.sh
    

    http_client.h:http客户端

    • http_client_send:同步客户端
    • http_client_send_async:异步客户端

    测试代码见:https://github.com/ithewei/libhv/blob/master/examples/curl.cpp
    测试步骤:

    make curl
    bin/curl -v www.example.com
    

    protocol各种常见协议

    protocol模块展示了各种常见协议的实现,可供学习参考。

    icmp.h:ping实现

    • ping:ping实现

    测试代码见:https://github.com/ithewei/libhv/blob/master/unittest/ping_test.c
    测试步骤:

    make unittest
    sudo bin/ping www.baidu.com
    

    dns.h:DNS域名查找

    • nslookup:dns查找

    测试代码见:https://github.com/ithewei/libhv/blob/master/unittest/nslookup_test.c
    测试步骤:

    make unittest
    bin/nslookup www.baidu.com
    

    ftp.h:FTP文件传输协议

    • ftp_connect:连接
    • ftp_login:登录
    • ftp_quit:退出
    • ftp_exec:执行命令
    • ftp_upload:上传文件
    • ftp_download:下载文件

    测试代码见:https://github.com/ithewei/libhv/blob/master/unittest/ftp_test.c

    smtp.h:SMTP邮件传输协议

    • sendmail:发送邮件

    测试代码见:https://github.com/ithewei/libhv/blob/master/unittest/sendmail_test.c

    last

    个人开发维护实属不易,如果觉得不错,请githubstar下,若libhv能带给你一点启发,吾将甚感欣慰!

    展开全文
  • libev,libhv

    2021-05-26 11:10:39
    git clone https://gitee.com/mirrors/libev.git git clone https://gitee.com/libhv/libhv.git
    git clone https://gitee.com/mirrors/libev.git
    git clone https://gitee.com/libhv/libhv.git

     

    展开全文
  • 包含libhv.dll libhv.lib 头文件,以及测试程序,
  • 纪念libhv上github trending

    千次阅读 2020-11-15 15:25:15
    文章目录libhv为何物libhv发展历程libhv的定位libhv和libevent、libev、libuv有什么不同libhv后续规划更多参考资料 libhv为何物 libhv是一个类似于libevent、libev、libuv的跨平台网络库,提供了更简单的接口和更...
  • libhv教程00--目录

    千次阅读 2021-02-07 01:21:54
    libhv是一个类似于libevent、libev、libuv的跨平台网络库,提供了更简单的接口和更丰富的协议。 项目地址:https://github.com/ithewei/libhv.git 码云镜像:https://gitee.com/ithewei/libhv.git QQ技术交流群:...
  • libhv踩坑记(一)

    2021-01-25 23:50:57
    我主要做一些在使用libhv时遇到的一些问题。好,入正题。 我的libhv框架是在2021年1月10日从github:https://github.com/ithewei/libhv/上下载下来的,并且非Debug编译生成了静态库来使用的。 编译环境:win10专业...
  • 像libevent, libev, and libuv , libhv提供具有非阻塞IO和计时器的事件循环,但具有更简单的api和更丰富的协议。 产品特点 跨平台(Linux,Windows,Mac,Solaris) 事件循环(IO,计时器,空闲) ENABLE_IPV6 ...
  • libhv libev libuv libevent 2020-08-28 最新源代码。这是几个经典的跨平台网络开发库
  • libhv教程01--介绍与体验

    千次阅读 2021-02-05 19:15:47
    libhv是一个类似于libevent、libev、libuv的跨平台网络库,提供了带非阻塞IO和定时器的事件循环。 libhv的名称也正是继承此派,寓意高性能的事件循环High-performance event loop library。 libhv能干什么 编写跨...
  • libhv---编译与安装

    2021-06-03 11:14:26
    libhv提供了原生Makefile(这里仅指适用于类unix系统的Makefile)和cmake两种构建方式。 根据开发者本人的博客进行编译安装,一直报错,测试发现是由于cmake中勾选了 BUILD_EXAMPLES 选项。不建议勾选案例,注意是...
  • libhv安装及example运行前言下载地址编译example执行修改example,重新编译总结 前言 libhv是一个类似于libevent、libev、libuv的跨平台网络库,提供了更简单的接口和更丰富的协议。 已经被为awesome-c和awesome-cpp...
  • 下文以TCP echo server为例,使用libhv创建TCP服务端。 文章目录c版本c++版本 c版本 代码示例参考tcp_echo_server.c #include "hv/hloop.h" void on_close(hio_t* io) { } void on_recv(hio_t* io, void* buf, int...
  • 近日,国产开源库libhv为awesome-c所收录,让我们看一下这个异军突起的libhv库究竟有何出色之处。 文章目录libhv简介比libevent、libuv更简单的API接口libhv提供的httpd性能媲美nginxlibhv是c++编写HTTP API服务端/...
  • libhv教程02--编译与安装

    千次阅读 2021-02-05 22:06:56
    libhv提供了原生Makefile(这里仅指适用于类unix系统的Makefile)和cmake两种构建方式。 为什么不额外提供vs工程或xcode工程?因为个人维护Makefile和cmake两套构建方式已经很累了,想将更多的时间和精力放在内容...
  • libhv 像libevent, libev, and libuv , libhv提供具有非阻塞IO和计时器的事件循环,但具有更简单的api和更丰富的协议。 :sparkles: 特征 跨平台(Linux,Windows,Mac,Solaris) EventLoop(IO,计时器,空闲)...
  • libhv中,用到了链表的数据结构(比如说,struct hloop_s中的 struct list_head idles),其定义和实现在list.h中。 首先看一下结构体: struct list_head { struct list_head *next, *prev; }; #define list_...
  • 解压后 cd libhv(压缩包解压后名字) ./configure make sudo make install 注:经过这三步编译出来的文件路径是/usr/local/include /usr/local/lib 这两个路径我们使用vs2019跨平台开发是访问不到的,需要手动改...
  • mingw32-make libhv libhv_static 至此,libhv编译成功,库文件在build\lib下 使用 新建一个console工程,将libhv项目的build文件夹中的include和lib文件夹复制到你的工程目录下,在pro文件中加上 LIBS += -L$$...
  • 上一篇分析了libhv里面用到的链表的实现,今天我们看一下定时器超时事件中用到的堆得实现。 堆定义 1.堆中的某个节点总是不大于或不小于其父亲节点 2.堆总是一颗完全二叉树 那么,最大堆就是父节点比每一个子节点...
  • 从本文开始,由sample入手,逐渐理解libhv的源码。 如有理解错误,欢迎批评指正。 main()函数 int main() { // memcheck atexit HV_MEMCHECK; hloop_t* loop = hloop_new(0); // test idle and priority for ...
  • 今天主要分析下libhv中的IO事件中的写事件的过程。 如有理解错误,欢迎批评指正。 还是先看接上一篇的源码 int main() { hloop_t* loop = hloop_new(0); // 客户端fd int connFd = ConnectTimeout("127.0.0.1",...

空空如也

空空如也

1 2 3 4 5 ... 11
收藏数 214
精华内容 85
关键字:

libhv