# libhv
**Repository Path**: pacelu-qt/libhv
## Basic Information
- **Project Name**: libhv
- **Description**: 🔥 比libevent、libuv更易用的国产网络库,用来开发
TCP/UDP/SSL/HTTP/WebSocket 客户端/服务端
- **Primary Language**: C/C++
- **License**: BSD-3-Clause
- **Default Branch**: master
- **Homepage**: https://github.com/ithewei/libhv
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 323
- **Created**: 2021-12-16
- **Last Updated**: 2023-05-21
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
English | [中文](README-CN.md)
# libhv
[](.github/workflows/CI.yml)
[](https://github.com/ithewei/libhv/actions/workflows/CI.yml?query=branch%3Amaster)
[](https://github.com/ithewei/libhv/actions/workflows/benchmark.yml?query=branch%3Amaster)
[](https://github.com/ithewei/libhv/releases)
[](https://github.com/ithewei/libhv/stargazers)
[](https://github.com/ithewei/libhv/network/members)
[](https://github.com/ithewei/libhv/issues)
[](https://github.com/ithewei/libhv/pulls)
[](LICENSE)
[](https://gitee.com/libhv/libhv)
[](https://github.com/oz123/awesome-c)
[](https://github.com/fffaraz/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, Solaris)
- High-performance EventLoop (IO, timer, idle, custom)
- TCP/UDP client/server/proxy
- TCP supports heartbeat, upstream, unpack, MultiThread-safe write and close, etc.
- 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 static file service, indexof service, sync/async API handler
- HTTP supports RESTful, URI router, keep-alive, chunked, etc.
- WebSocket client/server
## ⌛️ Build
see [BUILD.md](BUILD.md)
Makefile:
```shell
./configure
make
sudo make install
```
or cmake:
```shell
mkdir build
cd build
cmake ..
cmake --build .
```
or vcpkg:
```shell
vcpkg install libhv
```
or xmake:
```shell
xrepo install libhv
```
## ⚡️ Getting Started
run `./getting_started.sh`:
```shell
git clone https://github.com/ithewei/libhv.git
cd libhv
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 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
# 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](examples/tcp_echo_server.c)
```c
#include "hloop.h"
static void on_close(hio_t* io) {
printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io));
}
static void on_recv(hio_t* io, void* buf, int readbytes) {
// echo
hio_write(io, buf, readbytes);
}
static void on_accept(hio_t* io) {
hio_setcb_close(io, on_close);
hio_setcb_read(io, on_recv);
hio_read(io);
}
int main() {
int port = 1234;
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 -1;
}
hloop_run(loop);
hloop_free(&loop);
return 0;
}
```
**c++ version**: [evpp/TcpServer_test.cpp](evpp/TcpServer_test.cpp)
```c++
#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();
while (1) hv_sleep(1);
return 0;
}
```
#### tcp client
**c version**: [examples/nc.c](examples/nc.c)
```c
#include "hloop.h"
static void on_close(hio_t* io) {
printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io));
}
static void on_recv(hio_t* io, void* buf, int readbytes) {
printf("< %.*s\n", readbytes, (char*)buf);
}
static void on_connect(hio_t* io) {
hio_setcb_read(io, on_recv);
hio_read(io);
hio_write(io, "hello", 5);
}
int main() {
const char host[] = "127.0.0.1";
int port = 1234;
hloop_t* loop = hloop_new(0);
hio_t* io = hio_create_socket(loop, host, port, HIO_TYPE_TCP, HIO_CLIENT_SIDE);
if (io == NULL) {
perror("socket");
exit(1);
}
hio_setcb_connect(io, on_connect);
hio_setcb_close(io, on_close);
hio_connect(io);
hloop_run(loop);
hloop_free(&loop);
return 0;
}
```
**c++ version**: [evpp/TcpClient_test.cpp](evpp/TcpClient_test.cpp)
```c++
#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());
channel->write("hello");
} 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();
while (1) hv_sleep(1);
return 0;
}
```
### HTTP
#### http server
see [examples/http_server_test.cpp](examples/http_server_test.cpp)
**golang gin style**
```c++
#include "HttpServer.h"
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());
});
http_server_t server;
server.port = 8080;
server.service = &router;
http_server_run(&server);
return 0;
}
```
#### http client
see [examples/http_client_test.cpp](examples/http_client_test.cpp)
**python requests style**
```c++
#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;
}
```
**js axios style**
```c++
#include "axios.h"
int main() {
const char* strReq = R"({
"method": "POST",
"url": "http://127.0.0.1:8080/echo",
"params": {
"page_no": "1",
"page_size": "10"
},
"headers": {
"Content-Type": "application/json"
},
"body": {
"app_id": "123456",
"app_secret": "abcdefg"
}
})";
// sync
auto resp = axios::axios(strReq);
if (resp == NULL) {
printf("request failed!\n");
} else {
printf("%s\n", resp->body.c_str());
}
// async
int finished = 0;
axios::axios(strReq, [&finished](const HttpResponsePtr& resp) {
if (resp == NULL) {
printf("request failed!\n");
} else {
printf("%s\n", resp->body.c_str());
}
finished = 1;
});
// wait async finished
while (!finished) hv_sleep(1);
return 0;
}
```
## 🍭 More examples
### c version
- [examples/hloop_test.c](examples/hloop_test.c)
- [examples/tcp_echo_server.c](examples/tcp_echo_server.c)
- [examples/tcp_chat_server.c](examples/tcp_chat_server.c)
- [examples/tcp_proxy_server.c](examples/tcp_proxy_server.c)
- [examples/udp_echo_server.c](examples/udp_echo_server.c)
- [examples/udp_proxy_server.c](examples/udp_proxy_server.c)
- [examples/tinyhttpd.c](examples/tinyhttpd.c)
- [examples/jsonrpc](examples/jsonrpc)
- [examples/multi-thread/multi-acceptor-processes.c](examples/multi-thread/multi-acceptor-processes.c)
- [examples/multi-thread/multi-acceptor-threads.c](examples/multi-thread/multi-acceptor-threads.c)
- [examples/multi-thread/one-acceptor-multi-workers.c](examples/multi-thread/one-acceptor-multi-workers.c)
### c++ version
- [evpp/EventLoop_test.cpp](evpp/EventLoop_test.cpp)
- [evpp/EventLoopThread_test.cpp](evpp/EventLoopThread_test.cpp)
- [evpp/EventLoopThreadPool_test.cpp](evpp/EventLoopThreadPool_test.cpp)
- [evpp/TcpServer_test.cpp](evpp/TcpServer_test.cpp)
- [evpp/TcpClient_test.cpp](evpp/TcpClient_test.cpp)
- [evpp/UdpServer_test.cpp](evpp/UdpServer_test.cpp)
- [evpp/UdpClient_test.cpp](evpp/UdpClient_test.cpp)
- [examples/http_server_test.cpp](examples/http_server_test.cpp)
- [examples/http_client_test.cpp](examples/http_client_test.cpp)
- [examples/websocket_server_test.cpp](examples/websocket_server_test.cpp)
- [examples/websocket_client_test.cpp](examples/websocket_client_test.cpp)
- [examples/protorpc](examples/protorpc)
- [examples/qt](examples/qt)
### simulate well-known command line tools
- [examples/nc](examples/nc.c)
- [examples/nmap](examples/nmap)
- [examples/httpd](examples/httpd)
- [examples/wrk](examples/wrk.cpp)
- [examples/curl](examples/curl.cpp)
- [examples/wget](examples/wget.cpp)
- [examples/consul](examples/consul)
## 🥇 Benchmark
### tcp benchmark
```shell
cd echo-servers
./build.sh
./benchmark.sh
```
**throughput**:
```shell
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
```
### http benchmark
```shell
# 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)**

Above test results can be found on [Github Actions](https://github.com/ithewei/libhv/actions/workflows/benchmark.yml).