勿在浮沙筑高台
libevent组件
evutil
用于抽象不同平台网络实现差异的通用功能
event和event_bask
libevent的核心,为各种平台特定的、基于事件的非阻塞IO后端提供抽象API,让程序可以知道套接字何时已经准备好,可以读或者可以写,并且处理基本的超时功能,检测OS信号
bufferevent
为livent基于事件的核心提供使用更方便的封装。除了通知程序套接字已经准备好读写之外,还
可以让程序可以请求缓冲的读写操作,可以知道何时IO已经真正发生(bufferevent接口有多个后端,可以采用系统能够提供的更快的非阻塞IO方式,如Windows中的IOCP。)
evbuffer
在bufferevent层之下实现了缓冲功能,并且提供了方便有效的访问函数
evhttp
一个简单的HTTP客户端/服务器实现
evdns
一个简单的DNS客户端/服务器实现
evrpc
一个简单的RPC实现
libevent库
libevent_core
所有核心的事件和缓冲功能,包含了所有的event_base、evbuffer、bufferevent和工具函数
libevent_extra
定义了程序可能需要,也可能不需要的协议特定功能,包括HTTP、DNS和RPC
日志模块分析
提供了四个等级的日志
#define EVENT_LOG_DEBUG 0
#define EVENT_LOG_MSG 1
#define EVENT_LOG_WARN 2
#define EVENT_LOG_ERR 3
用户需要定义一个回调用来接收libevnt的日志消息。
typedef void (*event_log_cb)(int severity, const char *msg);
void event_set_log_callback(event_log_cb cb);
从参数可知,用户可以指定获取哪个等级的日志消息,以及指定一个用来接收日志的缓冲区 msg。
同时调用 event_set_log_callback
,将自定义的函数回调作为参数设置进去即可完成日志的获取。
进入 event_set_log_callback
函数,可以看到,libevent使用一个log_fn变量来接收用户自定义的日志回调函数。
当不想再继续接收日志信息的时候,重新调用event_set_log_callback
函数,参数给 nullptr 即可;
static event_log_cb log_fn = NULL;
static void
event_log(int severity, const char *msg)
{
if (log_fn)
log_fn(severity, msg);//如果用户注册了回调,则将相应等级的日志消息传递给用户
else {
const char *severity_str;
switch (severity) {
case _EVENT_LOG_DEBUG:
severity_str = "debug";
break;
case _EVENT_LOG_MSG:
severity_str = "msg";
break;
case _EVENT_LOG_WARN:
severity_str = "warn";
break;
case _EVENT_LOG_ERR:
severity_str = "err";
break;
default:
severity_str = "???";
break;
}
(void)fprintf(stderr, "[%s] %s\n", severity_str, msg);//打印日志信息
}
}
实例:
#include <event2/event.h>
static void GetLog(int severity, const char *msg)
{
const char *s;
switch (severity) {
case _EVENT_LOG_DEBUG: s = "debug"; break;
case _EVENT_LOG_MSG: s = "msg"; break;
case _EVENT_LOG_WARN: s = "warn"; break;
case _EVENT_LOG_ERR: s = "error"; break;
default: s = "?"; break; /* never reached */
}
//fprintf(logfile, "[%s] %s\n", s, msg);
std::cout << s << "====" << msg << std::endl;
}
event_set_log_callback(GetLog);//注册回调
不能在定义的日志回调中调用libevent的API函数,否则可能会发生未定义行为
日志相关函数
#ifdef __GNUC__
#define EV_CHECK_FMT(a,b) __attribute__((format(printf, a, b)))
#define EV_NORETURN __attribute__((noreturn))
#else
#define EV_CHECK_FMT(a,b)
#define EV_NORETURN
#endif//当编译器是GNU的时候才会进行格式匹配检测
void event_err(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3) EV_NORETURN;
void event_warn(const char *fmt, ...) EV_CHECK_FMT(1,2);
void event_sock_err(int eval, evutil_socket_t sock, const char *fmt, ...) EV_CHECK_FMT(3,4) EV_NORETURN;
void event_sock_warn(evutil_socket_t sock, const char *fmt, ...) EV_CHECK_FMT(2,3);
void event_errx(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3) EV_NORETURN;
void event_warnx(const char *fmt, ...) EV_CHECK_FMT(1,2);
format (archetype, string-index, first-to-check)
其中,“archetype”指定是哪种风格;“string-index”指定传入函数的第几个参数是格式化字符串;“first-to-check”指定从函数的第几个参数开始按上述规则进行检查。
void _event_debugx(const char *fmt, ...) EV_CHECK_FMT(1,2);
#define EV_CHECK_FMT(a,b) __attribute__((format(printf, a, b)))
上面就是日志的相关API函数。其中EV_CHECK_FMT
是一个宏,用来检测格式是否匹配
参考链接:https://blog.csdn.net/xiaozi0221/article/details/106893648
attribute format属性可以给被声明的函数加上类似printf或者scanf的特征,它可以使编译器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。format属性告诉编译器,按照printf, scanf等标准C函数参数格式规则对该函数的参数进行检查。这在我们自己封装调试信息的接口时非常的有用。
format (archetype, string-index, first-to-check)
其中,“archetype”指定是哪种风格;“string-index”指定传
入函数的第几个参数是格式化字符串;“first-to-check”指定从函
数的第几个参数开始按上述规则进行检查。
void event_warnx(const char *fmt, ...) EV_CHECK_FMT(1,2);
以此为例,传入的第一个参数是格式化字符串,从函数的第二个参数开始进行检查