Libevent:6辅助函数以及类型
2015-01-25 10:37
316 查看
在头文件<event2/util.h>中定义了许多有用的函数和类型来帮助实现可移植的程序。Libevent在内部使用这些类型和函数。
一:基本类型
evutil_socket_t
除了Windows之外的大多数系统,socket就是一个整数,而且操作系统按照数值顺序对它们进行处理。而在WindowssocketAPI中,socket是SOCKET类型,该类型是一个类似于指针的OS句柄,而且得到它们的顺序也是未定义的。Libevent定义evutil_socket_t类型为一个整数,该整数可以表示socket或者accept函数的返回值,并且可以在Windows上避免指针截断的风险。
#ifdefWIN32
#defineevutil_socket_tintptr_t
#else
#defineevutil_socket_tint
#endif
标准整数类型
有时你的C系统可能落后于21世纪,因此并没有实现C99标准的stdint.h头文件。这种情况下,Libevent自己定义了stdint.h中的,确定位宽度(bit-width-specific)的整数。
类似于C99标准,这些类型都有确定的位宽度。
各种兼容性类型
在那些具有ssize_t的类型的平台上,ev_ssize_t就被定义为ssize_t(signedsize_t),对于没有这种类型的平台,ev_ssize_t会被定义为合理的默认值。ev_ssize_t类型可能的最大值是EV_SSIZE_MAX;最小值为EV_SSIZE_MIN。(在平台没有定义SIZE_MAX的时候,size_t类型的最大可能值是EV_SIZE_MAX)
ev_off_t类型用来表示一个文件或一段内存中的偏移值。在那些具有合理的off_t类型定义的系统上,ev_off_t被定义为off_t,在Windows上被定义为ev_int4_t。
某些socketAPI的实现提供了长度类型socklen_t,而某些却没有提供。在那些提供该类型的平台上,ev_uintptr_t就被定义为socklen_t,而那些没有定义的平台,ev_socklen_t被定义为一个合理的默认值。
ev_intptr_t类型是一个,具有足够大的空间来保存一个指针而不会丢失位的有符号整数类型。ev_uintptr_t类型是一个,具有足够大的空间来保存一个指针而不会丢失位的无符号整数类型。
二:可移植的定时器函数
并不是所有的平台都定义了标准的timeval操作函数,所以Libevent提供了自己的实现。
#defineevutil_timeradd(tvp,uvp,vvp)/*...*/
#defineevutil_timersub(tvp,uvp,vvp)/*...*/
这些宏会将其前两个参数进行相加或相减,并将结果保存在第三个参数中。
#defineevutil_timerclear(tvp)/*...*/
#defineevutil_timerisset(tvp)/*...*/
#defineevutil_timercmp(tvp,uvp,cmp)
intevutil_gettimeofday(structtimeval*tv,structtimezone*tz);
structtimevaltv1,tv2,tv3;
/*Settv1=5.5seconds*/
tv1.tv_sec=5;tv1.tv_usec=500*1000;
/*Settv2=now*/
evutil_gettimeofday(&tv2,NULL);
/*Settv3=5.5secondsinthefuture*/
evutil_timeradd(&tv1,&tv2,&tv3);
/*all3shouldprinttrue*/
if(evutil_timercmp(&tv1,&tv1,==))/*=="Iftv1==tv1"*/
puts("5.5sec==5.5sec");
if(evutil_timercmp(&tv3,&tv2,>=))/*=="Iftv3>=tv2"*/
puts("Thefutureisafterthepresent.");
if(evutil_timercmp(&tv1,&tv2,<))/*=="Iftv1<tv2"*/
puts("Itisnolongerthepast.");
三:SocketAPI兼容性
由于历史原因,Windows从未真正的以良好的兼容性实现伯克利SocketAPI。下面的函数可以规避这种情况:
intevutil_closesocket(evutil_socket_ts);
#defineEVUTIL_CLOSESOCKET(s)evutil_closesocket(s)
这些函数用来关闭socket,在Unix上,它就是close函数的别名。在Windows上,它调用closesocket。(在Windows上,不能在socket上调用close,也没有其他系统定义closesocket函数。)
#defineEVUTIL_SOCKET_ERROR()
#defineEVUTIL_SET_SOCKET_ERROR(errcode)
#defineevutil_socket_geterror(sock)
#defineevutil_socket_error_to_string(errcode)
这些宏访问并操作socket错误码。EVUTIL_SOCKET_ERROR返回本线程最近一次的socket操作的全局错误码。evutil_socket_geterror针对某个特定socket做同样的事。(在类Unix系统上,全局错误码就是errno)。EVUTIL_SET_SOCKET_ERROR改变当前的socket错误码(类似于在Unix上设置errno),evutil_socket_error_to_string返回给定错误码的字符串描述。(类似于Unix上的strerror)
(之所以需要这些函数,是因为Windows没有为socket函数的错误定义errno,而是使用函数WSAGetLastError)
注意:在windows上,套接字错误码与标准C错误码errno是不同的。
intevutil_make_socket_nonblocking(evutil_socket_tsock);
对套接字IO的非阻塞设置也不能移植到Windows中。evutil_make_socket_nonblocking函数将一个新的socket描述符(由socket或accept返回),设置为非阻塞的。(在Unix上,置为O_NONBLOCK,在Windows上置为FIONBIO。)
intevutil_make_listen_socket_reuseable(evutil_socket_tsock);
该函数使得关闭一个监听套接字后,它使用的地址可以立即被另一个套接字使用。(在Unix上,是设置SO_REUSEADDR,而在Windows上,该标志却有其他意义。)
intevutil_make_socket_closeonexec(evutil_socket_tsock);
该函数使得操作系统在调用exec之后,会关闭该socket描述符。在Unix上是设置FD_CLOEXEC标志,而在Windows上什么也不做。
intevutil_socketpair(intfamily,inttype,intprotocol,evutil_socket_tsv[2]);
该函数类似于Unix上的socketpair函数:它在两个socket上创建一个相互连接的管道,而且可以使用普通的socketio函数。该函数将这两个socket保存在sv[0]和sv[1]中,该函数返回0表示成功,-1表示失败。
在Windows上,该函数仅支持AF_INET协议族,SOCK_STREAM类型以及协议0.注意:在某些Windows主机上,防火墙软件明确阻止127.0.0.1,禁止主机与自身通话的情况下,函数可能失败。
四:可移植的字符串操作函数
ev_int64_tevutil_strtoll(constchar*s,char**endptr,intbase)
该函数类似于strtol,但是可以处理64位整数。在某些平台上,它仅支持十进制。
intevutil_snprintf(char*buf,size_tbuflen,constchar*format,...);
intevutil_vsnprintf(char*buf,size_tbuflen,constchar*format,va_listap);
这些snprintf替代函数与标准的snprintf和vsnprintf接口类似。它们返回写入到buffer缓冲区的字节数,不包括末尾的NULL字节。(这种行为符合C99标准,但与Windows上的_snprintf不同,它在字符串无法放入缓冲区时,返回一个负数。)
五:区域无关的字符串处理函数
有时当实现基于ASCII的协议时,希望能够处理ASCII中字符类型概念上的字符串,而不管当前的区域配置。Libevent提供了一些这样的辅助函数。
intevutil_ascii_strcasecmp(constchar*str1,constchar*str2);
intevutil_ascii_strncasecmp(constchar*str1,constchar*str2,size_tn);
这些函数类似于strcasecmp和strncasecmp,但他们仅使用ASCII字符集进行比较,而不管当前的区域设置。
六:IPV6辅助函数和可移植函数
constchar*evutil_inet_ntop(intaf,constvoid*src,char*dst,size_tlen);
intevutil_inet_pton(intaf,constchar*src,void*dst);
这些函数类似于标准的inet_ntop和inet_pton函数,根据RFC3493中的规定,解析和格式化IPv4和IPv6地址。格式化一个IPv4地址,调用evutil_inet_ntop,af置为AF_INET,src指向in_addr结构,dst指向一个len长度的字符缓冲区。而对于IPv6地址,则af置为AF_INET6,src指向in6_addr结构。
要解析一个IPv4或一个IPv6地址,调用evutil_inet_pton函数,af置为AF_INET或者AF_INET6,src中是需要解析的字符串,dst指向一个in_addr或者in_addr6结构。
evutil_inet_ntop()失败时返回NULL,成功时返回指向dst的指针。evutil_inet_pton()函数成功时返回0,失败是返回-1。
intevutil_parse_sockaddr_port(constchar*str,structsockaddr*out,int*outlen);
该函数解析str中的字符串地址,并将结果写入到out中。outlen是一个值-结果参数,入参时指向一个out长度的整数,返回时变为实际使用的字节数。该方法成功时返回0,失败是返回-1.它可以处理下面格式的地址:
·[ipv6]:port(asin"[ffff::]:80")
·ipv6(asin"ffff::")
·[ipv6](asin"[ffff::]")
·ipv4:port(asin"1.2.3.4:80")
·ipv4(asin"1.2.3.4")
如果没有给定port,则在sockaddr的结果中,port被置为0.
intevutil_sockaddr_cmp(conststructsockaddr*sa1,
conststructsockaddr*sa2,intinclude_port);
evutil_sockaddr_cmp函数比较两个地址,如果sa1在sa2前面,则返回负数,如果它们相等就返回0,如果sa2在sa1前面,就返回正数。该函数可以处理AF_INET和AF_INET6地址族,而对于其他地址则是未定义的。它保证给出一个所有地址的总排序,但是不同的Libevent版本可能会有不同的排序结果。
如果include_port置为false,那么如果两个sockaddrs仅在port不同的情况下,该函数将他们视为相同的。否则,如果include_port置为true,则会视为不同的。
七:结构体可移植函数
#defineevutil_offsetof(type,field)/*...*/
类似于标准的offsetof宏,该宏返回field域在type中的偏移字节。
八:安全的随机数生成器
很多应用都需要一个难以预测的随机数源来保证它们的安全性。
voidevutil_secure_rng_get_bytes(void*buf,size_tn);
该接口将n个字节的随机数据填充到buf中。如果平台提供了arc4random函数,则Libevent会使用该函数。否则的话,Libevent使用自己实现的arc4random函数。种子则来自操作系统的熵池(entropypool)(Windows中的CryptGenRandom,其他平台中的/dev/urandom)
intevutil_secure_rng_init(void);
voidevutil_secure_rng_add_bytes(constchar*dat,size_tdatlen);
一般不需要手动初始化安全随机数生成器,但是如果需要保证它确实成功的初始化,可以调用evutil_secure_rng_init()函数。它会seedRNG(随机数生成器)(如果还没有seed过),并且在成功时返回0。如果返回-1,表明Libevent无法在系统上找到好的熵源,而且在没有自己初始化时,不能安全的使用RNG。
如果程序运行在可能会放弃权限的环境中(比如说,通过执行chroot()),在放弃权限前应该调用evutil_secure_rng_init()。
可以调用evutil_secure_rng_add_bytes()向熵池加入更多随机字节,但通常不需要这么做。
http://www.wangafu.net/~nickm/libevent-book/Ref5_evutil.html
一:基本类型
evutil_socket_t
除了Windows之外的大多数系统,socket就是一个整数,而且操作系统按照数值顺序对它们进行处理。而在WindowssocketAPI中,socket是SOCKET类型,该类型是一个类似于指针的OS句柄,而且得到它们的顺序也是未定义的。Libevent定义evutil_socket_t类型为一个整数,该整数可以表示socket或者accept函数的返回值,并且可以在Windows上避免指针截断的风险。
#ifdefWIN32
#defineevutil_socket_tintptr_t
#else
#defineevutil_socket_tint
#endif
标准整数类型
有时你的C系统可能落后于21世纪,因此并没有实现C99标准的stdint.h头文件。这种情况下,Libevent自己定义了stdint.h中的,确定位宽度(bit-width-specific)的整数。
Type | Width | Signed | Maximum | Minimum |
ev_uint64_t | 64 | No | EV_UINT64_MAX | 0 |
ev_int64_t | 64 | Yes | EV_INT64_MAX | EV_INT64_MIN |
ev_uint32_t | 32 | No | EV_UINT32_MAX | 0 |
ev_int32_t | 32 | Yes | EV_INT32_MAX | EV_INT32_MIN |
ev_uint16_t | 16 | No | EV_UINT16_MAX | 0 |
ev_int16_t | 16 | Yes | EV_INT16_MAX | EV_INT16_MIN |
ev_uint8_t | 8 | No | EV_UINT8_MAX | 0 |
ev_int8_t | 8 | Yes | EV_INT8_MAX | EV_INT8_MIN |
各种兼容性类型
在那些具有ssize_t的类型的平台上,ev_ssize_t就被定义为ssize_t(signedsize_t),对于没有这种类型的平台,ev_ssize_t会被定义为合理的默认值。ev_ssize_t类型可能的最大值是EV_SSIZE_MAX;最小值为EV_SSIZE_MIN。(在平台没有定义SIZE_MAX的时候,size_t类型的最大可能值是EV_SIZE_MAX)
ev_off_t类型用来表示一个文件或一段内存中的偏移值。在那些具有合理的off_t类型定义的系统上,ev_off_t被定义为off_t,在Windows上被定义为ev_int4_t。
某些socketAPI的实现提供了长度类型socklen_t,而某些却没有提供。在那些提供该类型的平台上,ev_uintptr_t就被定义为socklen_t,而那些没有定义的平台,ev_socklen_t被定义为一个合理的默认值。
ev_intptr_t类型是一个,具有足够大的空间来保存一个指针而不会丢失位的有符号整数类型。ev_uintptr_t类型是一个,具有足够大的空间来保存一个指针而不会丢失位的无符号整数类型。
二:可移植的定时器函数
并不是所有的平台都定义了标准的timeval操作函数,所以Libevent提供了自己的实现。
#defineevutil_timeradd(tvp,uvp,vvp)/*...*/
#defineevutil_timersub(tvp,uvp,vvp)/*...*/
这些宏会将其前两个参数进行相加或相减,并将结果保存在第三个参数中。
#defineevutil_timerclear(tvp)/*...*/
#defineevutil_timerisset(tvp)/*...*/
evutil_timerclear将一个timeval清空是将其值置为0。evutil_timerisset检查timeval,如果timeval的值为非0,则该宏返回true,否则返回false。
#defineevutil_timercmp(tvp,uvp,cmp)
evutil_timercmp比较两个timeval,并且如果它们之间的相对关系符合关系操作符cmp定义的比较关系的话,该宏返回true。比如:evutil_timercmp(t1,t2,<=)意味着“Ist1<=t2”。注意,不像某些操作系统,Libevent的timercmp支持所有的C关系操作符(即是<,>,==,!=,<=和>=)。
intevutil_gettimeofday(structtimeval*tv,structtimezone*tz);
evutil_gettimeofday函数设置tv为当前时间,参数tz无用。
structtimevaltv1,tv2,tv3;
/*Settv1=5.5seconds*/
tv1.tv_sec=5;tv1.tv_usec=500*1000;
/*Settv2=now*/
evutil_gettimeofday(&tv2,NULL);
/*Settv3=5.5secondsinthefuture*/
evutil_timeradd(&tv1,&tv2,&tv3);
/*all3shouldprinttrue*/
if(evutil_timercmp(&tv1,&tv1,==))/*=="Iftv1==tv1"*/
puts("5.5sec==5.5sec");
if(evutil_timercmp(&tv3,&tv2,>=))/*=="Iftv3>=tv2"*/
puts("Thefutureisafterthepresent.");
if(evutil_timercmp(&tv1,&tv2,<))/*=="Iftv1<tv2"*/
puts("Itisnolongerthepast.");
三:SocketAPI兼容性
由于历史原因,Windows从未真正的以良好的兼容性实现伯克利SocketAPI。下面的函数可以规避这种情况:
intevutil_closesocket(evutil_socket_ts);
#defineEVUTIL_CLOSESOCKET(s)evutil_closesocket(s)
这些函数用来关闭socket,在Unix上,它就是close函数的别名。在Windows上,它调用closesocket。(在Windows上,不能在socket上调用close,也没有其他系统定义closesocket函数。)
#defineEVUTIL_SOCKET_ERROR()
#defineEVUTIL_SET_SOCKET_ERROR(errcode)
#defineevutil_socket_geterror(sock)
#defineevutil_socket_error_to_string(errcode)
这些宏访问并操作socket错误码。EVUTIL_SOCKET_ERROR返回本线程最近一次的socket操作的全局错误码。evutil_socket_geterror针对某个特定socket做同样的事。(在类Unix系统上,全局错误码就是errno)。EVUTIL_SET_SOCKET_ERROR改变当前的socket错误码(类似于在Unix上设置errno),evutil_socket_error_to_string返回给定错误码的字符串描述。(类似于Unix上的strerror)
(之所以需要这些函数,是因为Windows没有为socket函数的错误定义errno,而是使用函数WSAGetLastError)
注意:在windows上,套接字错误码与标准C错误码errno是不同的。
intevutil_make_socket_nonblocking(evutil_socket_tsock);
对套接字IO的非阻塞设置也不能移植到Windows中。evutil_make_socket_nonblocking函数将一个新的socket描述符(由socket或accept返回),设置为非阻塞的。(在Unix上,置为O_NONBLOCK,在Windows上置为FIONBIO。)
intevutil_make_listen_socket_reuseable(evutil_socket_tsock);
该函数使得关闭一个监听套接字后,它使用的地址可以立即被另一个套接字使用。(在Unix上,是设置SO_REUSEADDR,而在Windows上,该标志却有其他意义。)
intevutil_make_socket_closeonexec(evutil_socket_tsock);
该函数使得操作系统在调用exec之后,会关闭该socket描述符。在Unix上是设置FD_CLOEXEC标志,而在Windows上什么也不做。
intevutil_socketpair(intfamily,inttype,intprotocol,evutil_socket_tsv[2]);
该函数类似于Unix上的socketpair函数:它在两个socket上创建一个相互连接的管道,而且可以使用普通的socketio函数。该函数将这两个socket保存在sv[0]和sv[1]中,该函数返回0表示成功,-1表示失败。
在Windows上,该函数仅支持AF_INET协议族,SOCK_STREAM类型以及协议0.注意:在某些Windows主机上,防火墙软件明确阻止127.0.0.1,禁止主机与自身通话的情况下,函数可能失败。
四:可移植的字符串操作函数
ev_int64_tevutil_strtoll(constchar*s,char**endptr,intbase)
该函数类似于strtol,但是可以处理64位整数。在某些平台上,它仅支持十进制。
intevutil_snprintf(char*buf,size_tbuflen,constchar*format,...);
intevutil_vsnprintf(char*buf,size_tbuflen,constchar*format,va_listap);
这些snprintf替代函数与标准的snprintf和vsnprintf接口类似。它们返回写入到buffer缓冲区的字节数,不包括末尾的NULL字节。(这种行为符合C99标准,但与Windows上的_snprintf不同,它在字符串无法放入缓冲区时,返回一个负数。)
五:区域无关的字符串处理函数
有时当实现基于ASCII的协议时,希望能够处理ASCII中字符类型概念上的字符串,而不管当前的区域配置。Libevent提供了一些这样的辅助函数。
intevutil_ascii_strcasecmp(constchar*str1,constchar*str2);
intevutil_ascii_strncasecmp(constchar*str1,constchar*str2,size_tn);
这些函数类似于strcasecmp和strncasecmp,但他们仅使用ASCII字符集进行比较,而不管当前的区域设置。
六:IPV6辅助函数和可移植函数
constchar*evutil_inet_ntop(intaf,constvoid*src,char*dst,size_tlen);
intevutil_inet_pton(intaf,constchar*src,void*dst);
这些函数类似于标准的inet_ntop和inet_pton函数,根据RFC3493中的规定,解析和格式化IPv4和IPv6地址。格式化一个IPv4地址,调用evutil_inet_ntop,af置为AF_INET,src指向in_addr结构,dst指向一个len长度的字符缓冲区。而对于IPv6地址,则af置为AF_INET6,src指向in6_addr结构。
要解析一个IPv4或一个IPv6地址,调用evutil_inet_pton函数,af置为AF_INET或者AF_INET6,src中是需要解析的字符串,dst指向一个in_addr或者in_addr6结构。
evutil_inet_ntop()失败时返回NULL,成功时返回指向dst的指针。evutil_inet_pton()函数成功时返回0,失败是返回-1。
intevutil_parse_sockaddr_port(constchar*str,structsockaddr*out,int*outlen);
该函数解析str中的字符串地址,并将结果写入到out中。outlen是一个值-结果参数,入参时指向一个out长度的整数,返回时变为实际使用的字节数。该方法成功时返回0,失败是返回-1.它可以处理下面格式的地址:
·[ipv6]:port(asin"[ffff::]:80")
·ipv6(asin"ffff::")
·[ipv6](asin"[ffff::]")
·ipv4:port(asin"1.2.3.4:80")
·ipv4(asin"1.2.3.4")
如果没有给定port,则在sockaddr的结果中,port被置为0.
intevutil_sockaddr_cmp(conststructsockaddr*sa1,
conststructsockaddr*sa2,intinclude_port);
evutil_sockaddr_cmp函数比较两个地址,如果sa1在sa2前面,则返回负数,如果它们相等就返回0,如果sa2在sa1前面,就返回正数。该函数可以处理AF_INET和AF_INET6地址族,而对于其他地址则是未定义的。它保证给出一个所有地址的总排序,但是不同的Libevent版本可能会有不同的排序结果。
如果include_port置为false,那么如果两个sockaddrs仅在port不同的情况下,该函数将他们视为相同的。否则,如果include_port置为true,则会视为不同的。
七:结构体可移植函数
#defineevutil_offsetof(type,field)/*...*/
类似于标准的offsetof宏,该宏返回field域在type中的偏移字节。
八:安全的随机数生成器
很多应用都需要一个难以预测的随机数源来保证它们的安全性。
voidevutil_secure_rng_get_bytes(void*buf,size_tn);
该接口将n个字节的随机数据填充到buf中。如果平台提供了arc4random函数,则Libevent会使用该函数。否则的话,Libevent使用自己实现的arc4random函数。种子则来自操作系统的熵池(entropypool)(Windows中的CryptGenRandom,其他平台中的/dev/urandom)
intevutil_secure_rng_init(void);
voidevutil_secure_rng_add_bytes(constchar*dat,size_tdatlen);
一般不需要手动初始化安全随机数生成器,但是如果需要保证它确实成功的初始化,可以调用evutil_secure_rng_init()函数。它会seedRNG(随机数生成器)(如果还没有seed过),并且在成功时返回0。如果返回-1,表明Libevent无法在系统上找到好的熵源,而且在没有自己初始化时,不能安全的使用RNG。
如果程序运行在可能会放弃权限的环境中(比如说,通过执行chroot()),在放弃权限前应该调用evutil_secure_rng_init()。
可以调用evutil_secure_rng_add_bytes()向熵池加入更多随机字节,但通常不需要这么做。
相关文章推荐
- libevent笔记-辅助函数/类型
- libevent参考手册第五章:辅助类型和函数 (七)
- LibEvent中文帮助文档--第9章【辅助类型和函数】
- Libevent应用 (四) 辅助类型和函数
- libevent参考手册第五章:辅助类型和函数
- libevent参考手册:辅助类型和函数(五)
- libevent参考手册第五章:辅助类型和函数
- [翻译]libevent参考手册第五章:辅助类型和函数
- 本附录介绍iOS系统包含的框架,它们为编写iOS平台的软件提供必要的接口。下面的表格尽可能地列出框架中的类、方法、函数、类型以及常量使用的关键前缀,请避免在您的符号名称中使用这些前缀。
- 六、辅助类型和函数
- Objective-C 2.0 with Cocoa Foundation--- 5,Class类型,选择器Selector以及函数指针
- Linux下socket编程的辅助函数:select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET
- 获取操作系统类型/版本以及是否是64位的函数
- 关于JavaScript预编译和执行顺序以及函数引用类型的思考
- cgic中常用的函数和变量以及返回类型 (可作为工具查询)
- Objective-C 2.0 with Cocoa Foundation--- 5,Class类型,选择器Selector以及函数指针
- time_t、struct tm,ctime各数据类型、函数详解,转换以及跟时间字符串的转换
- 对引用和指针使用以及函数返回引用和指针类型的理解
- 数组与字符串、字符指针与其他类型指针、赋值参数、指针参数以及引用参数、函数指针
- Sqlite3.3.8日期类型字段以及操作函数