libhv is a free, open source databases project written in C and released under BSD-3-Clause. It has 7,571 GitHub stars, 1,371 forks and 41 open issues, and was last pushed 6 days ago. On this registry it ranks #113 of 206 tracked projects in Databases, with 5 head-to-head comparisons available. It gained 17 stars over the last 30 tracked days.

What is libhv?

libhv is a cross-platform C/C++ network library for building TCP, UDP, SSL/TLS, HTTP, WebSocket, MQTT, and Redis clients and servers, written for developers who want the non-blocking event-loop model of libevent, libev, or libuv with a simpler API and more protocols built in.

What it is

libhv supplies an event loop with non-blocking IO and timers, the same foundation that libevent, libev, and libuv provide, but it ships a far wider protocol surface in the same package. It is written in C with a C++-usable interface and runs on Linux, Windows, macOS, Android, iOS, BSD, and Solaris, and its topic list points at epoll and IOCP as the platform readiness mechanisms it builds on. Licence is BSD-3-Clause. In this registry it is filed under Infrastructure & Operations / Databases, although its own scope is networking rather than storage; the Redis topic refers to the bundled client, not to a database engine.

The concrete problem is that a bare event loop stops at sockets and timers. Anyone who needs HTTP, WebSocket, MQTT, or Redis on top of one has to pull in additional libraries, each with its own threading rules, buffering, and TLS setup. libhv replaces that stack: HTTP, WebSocket, MQTT, and Redis clients are built in, TLS arrives through WITH_OPENSSL, WITH_GNUTLS, or WITH_MBEDTLS, and RUDP arrives through WITH_KCP, so a single dependency covers the transport and the protocols above it.

Key capabilities

  • High-performance EventLoop handling IO, timer, idle, custom, and signal events.
  • TCP/UDP client, server, and proxy, with TCP support for heartbeat, reconnect, upstream, and MultiThread-safe write and close.
  • Built-in common unpacking modes: FixedLength, Delimiter, and LengthField.
  • SSL/TLS support chosen at build time via WITH_OPENSSL, WITH_GNUTLS, or WITH_MBEDTLS, and RUDP support via WITH_KCP.
  • HTTP client and server supporting https, http1/x, http2, and grpc, with static service, indexof service, forward/reverse proxy service, and sync/async API handlers.
  • HTTP protocol features including RESTful, router, middleware, keep-alive, chunked, and SSE.
  • WebSocket client/server, plus an MQTT client and a Redis client.

Who uses it and how

  • C and C++ teams building network daemons that need HTTP, WebSocket, and MQTT from one dependency instead of three separate libraries.
  • Service authors writing TCP or UDP proxies and servers that need heartbeat, reconnect, or thread-safe write without hand-rolling those rules.
  • Cross-platform products targeting Android, iOS, BSD, or Solaris alongside Linux and Windows from the same codebase.
  • Evaluators running the bundled bin/httpd example as a file service, indexof service, and API service before integrating the library into their own build.
  • Projects needing RUDP/KCP transport for latency-sensitive traffic, enabled through the WITH_KCP build option.

Getting started

Build from source with ./configure && make && sudo make install, use cmake or bazel with bazel build libhv, or install the packaged artifact with vcpkg install libhv or xrepo install libhv. The README points to ./getting_started.sh and to binaries such as bin/httpd and bin/curl for a first run.

How it compares

The README positions libhv directly against libevent, libev, libuv, and asio: the same event-loop foundation with non-blocking IO and timers, but a simpler API and richer protocols. libevent, libev, libuv, and asio are named as the closest peers that a user would otherwise reach for, and the stated difference is that libhv includes the protocols rather than leaving them to be added separately. No paid products are listed as being replaced by this project.

When to use it — and when not to

A self-hoster has to compile the library and make build-time choices: TLS is inactive until WITH_OPENSSL, WITH_GNUTLS, or WITH_MBEDTLS is selected, and RUDP requires WITH_KCP, so the feature set depends on how the build is configured. No hosted or managed option appears in the facts, and the README is largely a feature list whose build details live in a separate BUILD.md and a wiki homepage, so the documentation is distributed rather than central. Teams that want only a bare event loop, or that need bindings outside C and C++, should look at the libraries named above instead.

project readme (upstream, from github) — read inline

English | 中文

libhv

Linux Windows macOS Android iOS benchmark
release stars forks issues PRs contributors license
gitee awesome-c awesome-cpp

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, macOS, Android, iOS, BSD, Solaris)
  • High-performance EventLoop (IO, timer, idle, custom, signal)
  • TCP/UDP client/server/proxy
  • TCP supports heartbeat, reconnect, upstream, MultiThread-safe write and close, etc.
  • Built-in common unpacking modes (FixedLength, Delimiter, LengthField)
  • RUDP support: WITH_KCP
  • SSL/TLS support: (via WITH_OPENSSL or WITH_GNUTLS or WITH_MBEDTLS)
  • HTTP client/server (support https http1/x http2 grpc)
  • HTTP supports static service, indexof service, forward/reverse proxy service, sync/async API handler
  • HTTP supports RESTful, router, middleware, keep-alive, chunked, SSE, etc.
  • WebSocket client/server
  • MQTT client
  • Redis client

⌛️ Build

see BUILD.md

Makefile:

./configure
make
sudo make install

or cmake:

mkdir build
cd build
cmake ..
cmake --build .

or bazel:

bazel build libhv

or vcpkg:

vcpkg install libhv

or xmake:

xrepo install libhv

⚡️ Getting Started

run ./getting_started.sh:

git clone https://github.com/ithewei/libhv.git
cd libhv
./configure
make

bin/httpd -h
bin/httpd -d
#bin/httpd -c etc/httpd.conf -s restart -d
ps aux | grep httpd

# http file 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' -F 'pswd=123456'
bin/curl -v localhost:8080/upload -d "@LICENSE"
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' -F 'int=123' -F 'float=3.14' -F 'string=hello'
# RESTful API: /group/:group_name/user/:user_id
bin/curl -v -X DELETE localhost:8080/group/test/user/123

# benchmark
bin/wrk -c 1000 -d 10 -t 4 http://127.0.0.1:8080/

TCP

tcp server

c version: examples/tcp_echo_server.c

c++ version: evpp/TcpServer_test.cpp

#include "TcpServer.h"
using namespace hv;

int main() {
    int port = 1234;
    TcpServer srv;
    int listenfd = srv.createsocket(port);
    if (listenfd < 0) {
        return -1;
    }
    printf("server listen on port %d, listenfd=%d ...\n", port, listenfd);
    srv.onConnection = [](const SocketChannelPtr& channel) {
        std::string peeraddr = channel->peeraddr();
        if (channel->isConnected()) {
            printf("%s connected! connfd=%d\n", peeraddr.c_str(), channel->fd());
        } else {
            printf("%s disconnected! connfd=%d\n", peeraddr.c_str(), channel->fd());
        }
    };
    srv.onMessage = [](const SocketChannelPtr& channel, Buffer* buf) {
        // echo
        channel->write(buf);
    };
    srv.setThreadNum(4);
    srv.start();

    // press Enter to stop
    while (getchar() != '\n');
    return 0;
}
tcp client

c version: examples/tcp_client_test.c

c++ version: evpp/TcpClient_test.cpp

#include <iostream>
#include "TcpClient.h"
using namespace hv;

int main() {
    int port = 1234;
    TcpClient cli;
    int connfd = cli.createsocket(port);
    if (connfd < 0) {
        return -1;
    }
    cli.onConnection = [](const SocketChannelPtr& channel) {
        std::string peeraddr = channel->peeraddr();
        if (channel->isConnected()) {
            printf("connected to %s! connfd=%d\n", peeraddr.c_str(), channel->fd());
        } else {
            printf("disconnected to %s! connfd=%d\n", peeraddr.c_str(), channel->fd());
        }
    };
    cli.onMessage = [](const SocketChannelPtr& channel, Buffer* buf) {
        printf("< %.*s\n", (int)buf->size(), (char*)buf->data());
    };
    cli.start();

    std::string str;
    while (std::getline(std::cin, str)) {
        if (str == "close") {
            cli.closesocket();
        } else if (str == "start") {
            cli.start();
        } else if (str == "stop") {
            cli.stop();
            break;
        } else {
            if (!cli.isConnected()) break;
            cli.send(str);
        }
    }
    return 0;
}

HTTP

http server

see examples/http_server_test.cpp

golang gin style

#include "HttpServer.h"
using namespace hv;

int main() {
    HttpService router;
    router.GET("/ping", [](HttpRequest* req, HttpResponse* resp) {
        return resp->String("pong");
    });

    router.GET("/data", [](HttpRequest* req, HttpResponse* resp) {
        static char data[] = "0123456789";
        return resp->Data(data, 10);
    });

    router.GET("/paths", [&router](HttpRequest* req, HttpResponse* resp) {
        return resp->Json(router.Paths());
    });

    router.GET("/get", [](HttpRequest* req, HttpResponse* resp) {
        resp->json["origin"] = req->client_addr.ip;
        resp->json["url"] = req->url;
        resp->json["args"] = req->query_params;
        resp->json["headers"] = req->headers;
        return 200;
    });

    router.POST("/echo", [](const HttpContextPtr& ctx) {
        return ctx->send(ctx->body(), ctx->type());
    });

    HttpServer server(&router);
    server.setPort(8080);
    server.setThreadNum(4);
    server.run();
    return 0;
}
http client

see examples/http_client_test.cpp

python requests style

#include "requests.h"

int main() {
    auto resp = requests::get("http://www.example.com");
    if (resp == NULL) {
        printf("request failed!\n");
    } else {
        printf("%s\n", resp->body.c_str());
    }

    resp = requests::post("127.0.0.1:8080/echo", "hello,world!");
    if (resp == NULL) {
        printf("request failed!\n");
    } else {
        printf("%s\n", resp->body.c_str());
    }

    return 0;
}

WebSocket

WebSocket server

see examples/websocket_server_test.cpp

#include "WebSocketServer.h"
using namespace hv;

int main(int argc, char** argv) {
    WebSocketService ws;
    ws.onopen = [](const WebSocketChannelPtr& channel, const HttpRequestPtr& req) {
        printf("onopen: GET %s\n", req->Path().c_str());
    };
    ws.onmessage = [](const WebSocketChannelPtr& channel, const std::string& msg) {
        printf("onmessage: %.*s\n", (int)msg.size(), msg.data());
    };
    ws.onclose = [](const WebSocketChannelPtr& channel) {
        printf("onclose\n");
    };

    WebSocketServer server(&ws);
    server.setPort(9999);
    server.setThreadNum(4);
    server.run();
    return 0;
}
WebSocket client

see examples/websocket_client_test.cpp

#include "WebSocketClient.h"
using namespace hv;

int main(int argc, char** argv) {
    WebSocketClient ws;
    ws.onopen = []() {
        printf("onopen\n");
    };
    ws.onmessage = [](const std::string& msg) {
        printf("onmessage: %.*s\n", (int)msg.size(), msg.data());
    };
    ws.onclose = []() {
        printf("onclose\n");
    };

    // reconnect: 1,2,4,8,10,10,10...
    reconn_setting_t reconn;
    reconn_setting_init(&reconn);
    reconn.min_delay = 1000;
    reconn.max_delay = 10000;
    reconn.delay_policy = 2;
    ws.setReconnect(&reconn);

    ws.open("ws://127.0.0.1:9999/test");

    std::string str;
    while (std::getline(std::cin, str)) {
        if (!ws.isConnected()) break;
        if (str == "quit") {
            ws.close();
            break;
        }
        ws.send(str);
    }

    return 0;
}

Redis

see examples/redis_client_test.cpp and examples/redis_subscriber_test.cpp

The Redis C++ module lives in the repository root redis/ and follows the same .h + .cpp split used by other libhv modules. Redis is disabled by default; enable it explicitly with ./configure --with-redis or cmake -DWITH_REDIS=ON.

./configure --with-redis
make unittest
cmake -S . -B build -DWITH_EVPP=ON -DWITH_REDIS=ON -DBUILD_UNITTEST=ON
cmake --build build --target redis_protocol_test redis_async_client_test redis_client_test redis_batch_test redis_subscriber_test

🍭 More examples

c version

c++ version

simulate well-known command line tools

🥇 Benchmark

pingpong echo-servers

cd echo-servers
./build.sh
./benchmark.sh

throughput:

libevent running on port 2001
libev running on port 2002
libuv running on port 2003
libhv running on port 2004
asio running on port 2005
poco running on port 2006

==============2001=====================================
[127.0.0.1:2001] 4 threads 1000 connections run 10s
total readcount=1616761 readbytes=1655563264
throughput = 157 MB/s

==============2002=====================================
[127.0.0.1:2002] 4 threads 1000 connections run 10s
total readcount=2153171 readbytes=2204847104
throughput = 210 MB/s

==============2003=====================================
[127.0.0.1:2003] 4 threads 1000 connections run 10s
total readcount=1599727 readbytes=1638120448
throughput = 156 MB/s

==============2004=====================================
[127.0.0.1:2004] 4 threads 1000 connections run 10s
total readcount=2202271 readbytes=2255125504
throughput = 215 MB/s

==============2005=====================================
[127.0.0.1:2005] 4 threads 1000 connections run 10s
total readcount=1354230 readbytes=1386731520
throughput = 132 MB/s

==============2006=====================================
[127.0.0.1:2006] 4 threads 1000 connections run 10s
total readcount=1699652 readbytes=1740443648
throughput = 165 MB/s

iperf tcp_proxy_server

# sudo apt install iperf
iperf -s -p 5001 > /dev/null &
bin/tcp_proxy_server 1212 127.0.0.1:5001 &
iperf -c 127.0.0.1 -p 5001 -l 8K
iperf -c 127.0.0.1 -p 1212 -l 8K

Bandwidth:

------------------------------------------------------------
[  3] local 127.0.0.1 port 52560 connected with 127.0.0.1 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  20.8 GBytes  17.9 Gbits/sec

------------------------------------------------------------
[  3] local 127.0.0.1 port 48142 connected with 127.0.0.1 port 1212
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  11.9 GBytes  10.2 Gbits/sec

webbench

# sudo apt install wrk
wrk -c 100 -t 4 -d 10s http://127.0.0.1:8080/

# sudo apt install apache2-utils
ab -c 100 -n 100000 http://127.0.0.1:8080/

libhv(port:8080) vs nginx(port:80)

libhv-vs-nginx.png

Above test results can be found on Github Actions.

Frequently asked questions

Is libhv free to use?

libhv is open source under the BSD-3-Clause licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does libhv do?

🔥 比libevent/libuv/asio更易用的网络库。A c/c++ network library for developing TCP/UDP/SSL/HTTP/WebSocket/MQTT/Redis client/server.

What is libhv written in?

libhv is primarily written in C. Its source is publicly available at https://github.com/ithewei/libhv, and it has 7,571 GitHub stars.