English | 中文
libhv
是一个类似于libevent、libev、libuv
的跨平台网络库,提供了更易用的接口和更丰富的协议。
- 跨平台(Linux, Windows, MacOS, Solaris)
- 高性能事件循环(网络IO事件、定时器事件、空闲事件、自定义事件)
- TCP/UDP服务端/客户端/代理
- TCP支持心跳、转发、拆包、多线程安全write和close等特性
- SSL/TLS加密通信(可选WITH_OPENSSL、WITH_GNUTLS、WITH_MBEDTLS)
- HTTP服务端/客户端(支持https http1/x http2 grpc)
- HTTP支持静态文件服务、目录服务、同步/异步API处理函数
- HTTP支持RESTful风格、URI路由、keep-alive长连接、chunked分块等特性
- WebSocket服务端/客户端
libhv提供了以下构建方式:
1、通过Makefile:
./configure
make
sudo make install
2、通过cmake:
mkdir build
cd build
cmake ..
cmake --build .
3、通过vcpkg:
vcpkg install libhv
4、通过xmake:
xrepo install libhv
运行脚本./getting_started.sh
:
# 下载编译
git clone https://github.com/ithewei/libhv.git
cd libhv
make
# 运行httpd服务
bin/httpd -h
bin/httpd -d
#bin/httpd -c etc/httpd.conf -s restart -d
ps aux | grep httpd
# 文件服务
bin/curl -v localhost:8080
# 目录服务
bin/curl -v localhost:8080/downloads/
# API服务
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
# 压力测试
bin/wrk -c 1000 -d 10 -t 4 http://127.0.0.1:8080/
c版本: examples/tcp_echo_server.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++版本: 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();
while (1) hv_sleep(1);
return 0;
}
c版本: examples/nc.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++版本: evpp/TcpClient_test.cpp
#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;
}
见examples/http_server_test.cpp
golang gin 风格
#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;
}
见examples/http_client_test.cpp
python requests 风格
#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 风格
#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;
}
- 事件循环: examples/hloop_test.c
- TCP回显服务: examples/tcp_echo_server.c
- TCP聊天服务: examples/tcp_chat_server.c
- TCP代理服务: examples/tcp_proxy_server.c
- UDP回显服务: examples/udp_echo_server.c
- UDP代理服务: examples/udp_proxy_server.c
- jsonRPC示例: examples/jsonrpc
- 多accept进程模式: examples/multi-thread/multi-acceptor-processes.c
- 多accept线程模式: examples/multi-thread/multi-acceptor-threads.c
- 一个accept线程+多worker线程: examples/multi-thread/one-acceptor-multi-workers.c
- 事件循环: evpp/EventLoop_test.cpp
- 事件循环线程: evpp/EventLoopThread_test.cpp
- 事件循环线程池: evpp/EventLoopThreadPool_test.cpp
- TCP服务端: evpp/TcpServer_test.cpp
- TCP客户端: evpp/TcpClient_test.cpp
- UDP服务端: evpp/UdpServer_test.cpp
- UDP客户端: evpp/UdpClient_test.cpp
- HTTP服务端: examples/http_server_test.cpp
- HTTP客户端: examples/http_client_test.cpp
- WebSocket服务端: examples/websocket_server_test.cpp
- WebSocket客户端: examples/websocket_client_test.cpp
- protobufRPC示例: examples/protorpc
- Qt中使用libhv示例: examples/qt
- 网络连接工具: examples/nc
- 网络扫描工具: examples/nmap
- HTTP服务程序: examples/httpd
- HTTP压测工具: examples/wrk
- URL请求工具: examples/curl
- 文件下载工具: examples/wget
- 服务注册与发现: examples/consul
cd echo-servers
./build.sh
./benchmark.sh
吞吐量:
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
# 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)
以上测试结果可以在 Github Actions 中查看。
- libhv QQ群:
739352073
,欢迎加群交流 - libhv 教程: https://hewei.blog.csdn.net/article/details/113733758
- libhv教程01--介绍与体验
- libhv教程02--编译与安装
- libhv教程03--链库与使用
- libhv教程04--编写一个完整的命令行程序
- libhv教程05--事件循环以及定时器的简单使用
- libhv教程06--创建一个简单的TCP服务端
- libhv教程07--创建一个简单的TCP客户端
- libhv教程08--创建一个简单的UDP服务端
- libhv教程09--创建一个简单的UDP客户端
- libhv教程10--创建一个简单的HTTP服务端
- libhv教程11--创建一个简单的HTTP客户端
- libhv教程12--创建一个简单的WebSocket服务端
- libhv教程13--创建一个简单的WebSocket客户端
- libhv教程14--200行实现一个纯C版jsonrpc框架
- libhv教程15--200行实现一个C++版protorpc框架
- libhv教程16--多线程/多进程服务端编程
- libhv教程17--Qt中使用libhv
如果您在使用libhv
,欢迎通过PR将信息提交至此列表,让更多的用户了解libhv
的实际使用场景,以建立更好的网络生态。
用户 (公司名/项目名/个人联系方式) | 案例 (项目简介/业务场景) |
---|---|
阅面科技 | 猎户AIoT平台设备管理、人脸检测HTTP服务、人脸搜索HTTP服务 |
socks5-libhv | socks5代理 |
hvloop | 类似uvloop的python异步IO事件循环 |