文章目录
<event2/util.h>定义了很多在实现可移植应用时有用的函数,libevent内部也使用这些类型和函数。
基本类型
1、evutil_socket_t
在除Windows之外的大多数地方,套接字是个整数,操作系统按照数值次序进行处理。然而,使用Windows套接字API时,socket具有类型SOCKET,它实际上是个类似指针的句柄,收到这个句柄的次序是未定义的。在Windows中,libevent定义evutil_socket_t类型为整型指针,可以处理socket()或者accept()的输出,而没有指针截断的风险。
定义:
#ifdef WIN32
#define evutil_socket_t intptr_t
#else
#define evutil_socket_t int
#endif
定时器可移植函数
不是每个平台都定义了标准timeval操作函数,所以libevent也提供了自己的实现
接口:
//分别对前两个参数进行加或者减运算,将结果存放到第三个参数中
#define evutil_timeradd(tvp, uvp, vvp) /* ...*/
#define evutil_timersub(tvp, uvp, vvp) /* ...*/
接口:
//清除 timeval会将其值设置为0。
#define evutil_timerclear(tvp) /* ...*/
//判断timeval是否为0;如果是0则返回false,否则返回true
#define evutil_timerisset(tvp) /* ...*/
接口:
//比较两个timeval
//这样使用:
//evutil_timercmp(t1,t2,<=)含义为:判断"t1<=t2”是否成立
//如果其关系满足cmp关系运算符,返回true
//cmp为所有C关系运算符(也就是<、>、==、!=、<=和>=)
#define evutil_timercmp(tvp, uvp, cmp)
接口:
//获取当前时间并保存到tv;
//tz目前无用
int evutil_gettimeofday(struct timeval* tv, struct timezone * tz);
套接字API兼容性
1、关闭套接字
接口:
#define EVUTIL_CLOSESOCKET(s) evutil_closesocket(s)
//在使用该函数之前,需要定义上面的那个宏
int evutil_closesocket(evutil_socket_t s);
在Unix中,它是close()的别名;在Windows中,它调用closesocket()。
在Windows中不能将close()用于套接字,也没有其他系统定义了closesocket()
这些宏访问和操作套接字错误代码。
宏 | 含义 |
---|---|
#define EVUTIL_SOCKET_ERROR() | 返回当前线程最后一次套接字操作的全局错误号 |
#define EVUTIL_SET_SOCKET_ERROR(errcode) | 修改当前套接字的错误号(与设置Unix中的errno类似) |
#define evutil_socket_geterror(sock) | 返回特定的套接字的错误号 (在类 Unix系统中都是errno) |
#define evutil_socket_error_to_string(errcode) | 返回代表某给定套接字错误号的字符串(与Unix中的strerror()类似) |
因为对于来自套接字函数的错误, Windows不使用errno,而是使用WSAGetLastError() ,所以需要这些函数。
注意:Windows套接字错误与从errno看到的标准C错误是不同的。
2、设置socket为非阻塞状态
接口:
int evutil_make_socket_nonblocking(evutil_socket_t sock);
用于对套接字进行非阻塞IO的调用也不能移植到Windows中。
该函数要求一个套接字【来自socket()或accept()函数】作为参数,将其设置为非阻塞的。
3、设置套接字地址可重用
接口:
int evutil_make_listen_socket_reuseable(evutil_socket_t sock);
确保关闭监听套接字后,它使用的地址可以立即被另一个套接字使用。
多个套接字绑定到相同地址
(在Unix中它设置SO_REUSEADDR标志,在Windows中则不做任何操作。不能在Windows中使用SO_REUSEADDR标志,它有另外不同的含义)
可移植的字符串操作函数
接口:
int evutil_snprintf(char* buf, size_t buflen, const char * format, ...);
int evutil_vsnprintf(char* buf, size_t buflen, const char * format, va_list ap);
这些snprintf替代函数的行为与标准snprintf和vsnprintf接口相同。
函数返回在缓冲区足够长的情况下将写入的字节数,不包括结尾的NULL字节。(这个行为遵循C99的snprintf()标准,但与Windows的_snprintf()相反:如果字符串无法放入缓冲区,_snprintf()会返回负数)
安全随机数发生器
接口:
void evutil_secure_rng_get_bytes(void* buf, size_t n);
这个函数用随机数据填充buf处的n个字节。
日志配置
Libevent能记录内部的错误和警告日志,如果编译进日志支持功能,也会记录调试信息。默认情况下这些消息都是输出到stderr,你也可以通过提供自己的日志函数的方法来覆盖这种行为。
接口:
#define EVENT_LOG_DEBUG 0
#define EVENT_LOG_MSG 1
#define EVENT_LOG_WARN 2
#define EVENT_LOG_ERR 3
/* Deprecated; see note at the end of this section*/
#define _EVENT_LOG_DEBUG EVENT_LOG_DEBUG
#define _EVENT_LOG_MSG EVENT_LOG_MSG
#define _EVENT_LOG_WARN EVENT_LOG_WARN
#define _EVENT_LOG_ERR EVENT_LOG_ERR
typedef void ( * event_log_cb)(int severity, const char* msg);
void event_set_log_callback(event_log_cb cb);
为了覆盖Libevent的日志行为,你需要自己编写满足event_log_cb格式的函数,然后将函数作为参数传入event_set_log_callback()。
无论什么时候只要Libevent需要写一个日志,都会进入到你提供的日志函数。
再次调用event_set_log_callback()并传一个NULL,可让日志功能再次回到默认功能。
例子:
#include <event2/event.h>
#include <stdio.h>
static void discard_cb(int severity, const char* msg)
{
/* This callback does nothing.*/
}
static FILE* logfile = NULL;
static void write_to_file_cb(int severity, const char* msg)
{
const char* s;
if (!logfile)
return;
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);
}
/* Turn off all logging from Libevent.*/
void suppress_logging(void)
{
event_set_log_callback(discard_cb);
}
/* Redirect all Libevent log messages to the C stdio file ‘f’.*/
void set_logfile(FILE* f)
{
logfile = f;
event_set_log_callback(write_to_file_cb);
}
注意:在用户提供的event_log_cb回调函数中进行Libevent的函数调用是不安全的。如果你想写一个用bufferevents去发送告警信息给一个网络socket的日志回调函数,你就可能会遇到非常奇怪和难以诊断的错误。
通常调试日志都是禁用的,也都不会发送给日志回调函数,但是如果libevent是编译成支持打开调试日志,你就可以手动打开调试日志。