此文归纳整理和libhv 源码分析相关的文章,献给感兴趣钻研libhv 源码的同学。 如有其他同学有写过不错的libhv 源码分析文章,欢迎联系我加上。
libhv 是一个比libevent、libev、libuv 更易用的跨平台c/c++ 国产网络库,用来开发TCP/UDP/SSL/HTTP/WebSocket/MQTT 客户端/服务端。
项目地址:https://github.com/ithewei/libhv.git 码云镜像:https://gitee.com/libhv/libhv.git QQ技术交流群:739352073
事件循环和IO多路复用机制介绍
https://hewei.blog.csdn.net/article/details/113724474
事件循环是libevent、libev、libuv、libhv 这类网络库里最核心的概念,即在事件循环里处理IO读写事件、定时器事件、自定义事件等各种事件;
IO多路复用即在一个IO线程监听多个fd,如最早期的select 、后来的poll ,linux的epoll 、windows的iocp 、bsd的kqueue 、solaris的port 等,都属于IO多路复用机制。非阻塞NIO搭配IO多路复用机制就是高并发的钥匙 。
libhv 下的event模块正是封装了多种平台的IO多路复用机制,提供了统一的事件接口 ,是libhv 的核心模块。
事件的定义以及数据结构介绍
libhv中的事件包括IO事件 、timer定时器事件 、idle空闲事件 、自定义事件 。 所有事件都是继承自公共的基类hevent_t ,便于放入事件队列中统一调度。
事件结构体hevent_t 定义在事件循环模块对外头文件 hloop.h 里:
struct hevent_s {
hloop_t* loop;
hevent_type_e event_type;
uint64_t event_id;
hevent_cb cb;
void* userdata;
void* privdata;
struct hevent_s* pending_next;
int priority;
};
事件循环结构体hloop_t 定义在内部 hevent.h 头文件里:
struct hloop_s {
uint32_t flags;
hloop_status_e status;
uint64_t start_ms;
uint64_t start_hrtime;
uint64_t end_hrtime;
uint64_t cur_hrtime;
uint64_t loop_cnt;
long pid;
long tid;
void* userdata;
uint32_t intern_nevents;
uint32_t nactives;
uint32_t npendings;
hevent_t* pendings[HEVENT_PRIORITY_SIZE];
struct list_head idles;
uint32_t nidles;
struct heap timers;
uint32_t ntimers;
struct io_array ios;
uint32_t nios;
hbuf_t readbuf;
void* iowatcher;
int eventfds[2];
event_queue custom_events;
hmutex_t custom_events_mutex;
};
事件的优先级机制
https://blog.csdn.net/qu1993/article/details/111194770
IO事件
定时器事件
https://blog.csdn.net/qu1993/article/details/111197841
空闲事件
https://blog.csdn.net/qu1993/article/details/111275642
自定义事件
https://blog.csdn.net/qu1993/article/details/111297130
心跳和保活机制
https://blog.csdn.net/qu1993/article/details/111246515
TCP如何处理粘包与分包
libhv 提供了设置拆包规则接口,c接口见hio_set_unpack ,c++接口见SocketChannel::setUnpack ,支持固定包长、分隔符、头部长度字段 三种常见的拆包方式,调用该接口设置拆包规则后,内部会根据拆包规则处理粘包与分包,保证回调上来的是完整的一包数据,大大节省了上层处理粘包与分包的成本,该接口具体定义如下:
typedef enum {
UNPACK_BY_FIXED_LENGTH = 1,
UNPACK_BY_DELIMITER = 2,
UNPACK_BY_LENGTH_FIELD = 3,
} unpack_mode_e;
#define DEFAULT_PACKAGE_MAX_LENGTH (1 << 21)
#define PACKAGE_MAX_DELIMITER_BYTES 8
typedef enum {
ENCODE_BY_VARINT = 1,
ENCODE_BY_LITTEL_ENDIAN = LITTLE_ENDIAN,
ENCODE_BY_BIG_ENDIAN = BIG_ENDIAN,
} unpack_coding_e;
typedef struct unpack_setting_s {
unpack_mode_e mode;
unsigned int package_max_length;
unsigned int fixed_length;
unsigned char delimiter[PACKAGE_MAX_DELIMITER_BYTES];
unsigned short delimiter_bytes;
unsigned short body_offset;
unsigned short length_field_offset;
unsigned short length_field_bytes;
unpack_coding_e length_field_coding;
#ifdef __cplusplus
unpack_setting_s() {
mode = UNPACK_BY_LENGTH_FIELD;
package_max_length = DEFAULT_PACKAGE_MAX_LENGTH;
fixed_length = 0;
delimiter_bytes = 0;
body_offset = 5;
length_field_offset = 1;
length_field_bytes = 4;
length_field_coding = ENCODE_BY_BIG_ENDIAN;
}
#endif
} unpack_setting_t;
HV_EXPORT void hio_set_unpack(hio_t* io, unpack_setting_t* setting);
以ftp 为例(分隔符方式)可以这样设置:
unpack_setting_t ftp_unpack_setting;
memset(&ftp_unpack_setting, 0, sizeof(unpack_setting_t));
ftp_unpack_setting.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH;
ftp_unpack_setting.mode = UNPACK_BY_DELIMITER;
ftp_unpack_setting.delimiter[0] = '\r';
ftp_unpack_setting.delimiter[1] = '\n';
ftp_unpack_setting.delimiter_bytes = 2;
以mqtt 为例(头部长度字段方式)可以这样设置:
unpack_setting_t mqtt_unpack_setting = {
.mode = UNPACK_BY_LENGTH_FIELD,
.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH,
.body_offset = 2,
.length_field_offset = 1,
.length_field_bytes = 1,
.length_field_coding = ENCODE_BY_VARINT,
};
具体实现代码在 event/unpack.c 中,在内部readbuf 的基础上直接原地拆包与组包,基本做到零拷贝,比抛给上层处理更高效,感兴趣的可以研究一下。
具体示例可参考 examples/jsonrpc、examples/protorpc
回显、聊天、代理三种经典服务实现详解
https://hewei.blog.csdn.net/article/details/115332093
回显、聊天、代理是每个后端工程师必须掌握的基础demo,开发服务端程序无非是基于这三种经典服务做业务开发;
多线程/多进程服务端编程
https://hewei.blog.csdn.net/article/details/120366024
为了充分利用现代计算机的多核处理器,掌握多线程服务端编程也就必不可少了。
libhv 的 examples/multi-thread 目录下给出了几种常见的多线程/多进程模式的具体写法:
multi-acceptor-processes :多accept进程模式multi-acceptor-threads :多accept线程模式one-acceptor-multi-workers :一个accept线程+多worker线程
网络编程十宗罪
https://hewei.blog.csdn.net/article/details/121313149
网络编程过程中十条最容易踩的坑,我称之为网络编程十宗罪,名字取的比较骇人,也是为了警醒大家在网络编程过程中切勿触犯以上条例,做到按例排查,写出更为健壮的网络程序。
致谢
last
最后祝愿大家都能通过libhv 掌握网络编程,欢迎大家加入libhv 的开源贡献中来,完善libhv 的网络生态。想参与贡献的可参考 libhv后续规划。
|