在頭文件<event2/util.h>中定義了許多有用的函數和類型來幫助實現可移植的程序。Libevent在內部使用這些類型和函數。html
一:基本類型windows
evutil_socket_t安全
除了Windows以外的大多數系統,socket就是一個整數,並且操做系統按照數值順序對它們進行處理。而在Windows socket API中,socket是SOCKET類型,該類型是一個相似於指針的OS句柄,並且獲得它們的順序也是未定義的。Libevent定義evutil_socket_t類型爲一個整數,該整數能夠表示socket或者accept函數的返回值,而且能夠在Windows上避免指針截斷的風險。dom
#ifdef WIN32socket
#define evutil_socket_t intptr_t函數
#elseui
#define evutil_socket_t intspa
#endif操作系統
標準整數類型.net
有時你的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 |
相似於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。
某些socket API的實現提供了長度類型socklen_t,而某些卻沒有提供。在那些提供該類型的平臺上,ev_uintptr_t就被定義爲socklen_t,而那些沒有定義的平臺,ev_socklen_t被定義爲一個合理的默認值。
ev_intptr_t類型是一個,具備足夠大的空間來保存一個指針而不會丟失位的有符號整數類型。ev_uintptr_t類型是一個,具備足夠大的空間來保存一個指針而不會丟失位的無符號整數類型。
二:可移植的定時器函數
並非全部的平臺都定義了標準的timeval操做函數,因此Libevent提供了本身的實現。
#define evutil_timeradd(tvp, uvp, vvp) /* ... */
#define evutil_timersub(tvp, uvp, vvp) /* ... */
這些宏會將其前兩個參數進行相加或相減,並將結果保存在第三個參數中。
#define evutil_timerclear(tvp) /* ... */
#define evutil_timerisset(tvp) /* ... */
evutil_timerclear將一個timeval清空是將其值置爲0。evutil_timerisset檢查timeval,若是timeval的值爲非0,則該宏返回true,不然返回false。
#define evutil_timercmp(tvp, uvp, cmp)
evutil_timercmp比較兩個timeval,而且若是它們之間的相對關係符合關係操做符cmp定義的比較關係的話,該宏返回true。好比:evutil_timercmp(t1, t2, <=)意味着「Is t1 <= t2」。注意,不像某些操做系統,Libevent的timercmp支持全部的C關係操做符(便是<, >, ==, !=, <= 和 >=)。
int evutil_gettimeofday(struct timeval *tv, struct timezone*tz);
evutil_gettimeofday函數設置tv爲當前時間,參數tz無用。
struct timeval tv1, tv2, tv3;
/* Settv1 = 5.5 seconds */
tv1.tv_sec= 5; tv1.tv_usec = 500*1000;
/*Set tv2 = now */
evutil_gettimeofday(&tv2, NULL);
/* Settv3 = 5.5 seconds in the future */
evutil_timeradd(&tv1,&tv2, &tv3);
/*all 3 should print true */
if(evutil_timercmp(&tv1, &tv1, ==)) /* == "If tv1 == tv1" */
puts("5.5 sec == 5.5 sec");
if(evutil_timercmp(&tv3, &tv2, >=)) /* == "If tv3 >= tv2" */
puts("The future is after thepresent.");
if(evutil_timercmp(&tv1, &tv2, <)) /* == "If tv1 < tv2" */
puts("It is no longer the past.");
三:Socket API兼容性
因爲歷史緣由,Windows從未真正的以良好的兼容性實現伯克利Socket API。下面的函數能夠規避這種狀況:
int evutil_closesocket(evutil_socket_t s);
#define EVUTIL_CLOSESOCKET(s) evutil_closesocket(s)
這些函數用來關閉socket,在Unix上,它就是close函數的別名。在Windows上,它調用closesocket。(在Windows上,不能在socket上調用close,也沒有其餘系統定義closesocket函數。)
#define EVUTIL_SOCKET_ERROR()
#define EVUTIL_SET_SOCKET_ERROR(errcode)
#define evutil_socket_geterror(sock)
#define evutil_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是不一樣的。
int evutil_make_socket_nonblocking(evutil_socket_t sock);
對套接字IO的非阻塞設置也不能移植到Windows中。evutil_make_socket_nonblocking函數將一個新的socket描述符(由socket或accept返回),設置爲非阻塞的。(在Unix上,置爲O_NONBLOCK,在Windows上置爲FIONBIO。)
int evutil_make_listen_socket_reuseable(evutil_socket_t sock);
該函數使得關閉一個監聽套接字後,它使用的地址能夠當即被另外一個套接字使用。(在Unix上,是設置SO_REUSEADDR,而在Windows上,該標誌卻有其餘意義。)
int evutil_make_socket_closeonexec(evutil_socket_t sock);
該函數使得操做系統在調用exec以後,會關閉該socket描述符。在Unix上是設置FD_CLOEXEC 標誌,而在Windows上什麼也不作。
int evutil_socketpair(int family, int type, int protocol, evutil_socket_t sv[2]);
該函數相似於Unix上的socketpair函數:它在兩個socket上建立一個相互鏈接的管道,並且可使用普通的socket io函數。該函數將這兩個socket保存在sv[0]和sv[1]中,該函數返回0表示成功,-1表示失敗。
在Windows上,該函數僅支持AF_INET協議族,SOCK_STREAM類型以及協議0. 注意:在某些Windows主機上,防火牆軟件明確阻止127.0.0.1,禁止主機與自身通話的狀況下,函數可能失敗。
四:可移植的字符串操做函數
ev_int64_t evutil_strtoll(const char *s, char **endptr, int base)
該函數相似於strtol,可是能夠處理64位整數。在某些平臺上,它僅支持十進制。
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接口相似。它們返回寫入到buffer緩衝區的字節數,不包括末尾的NULL字節。(這種行爲符合C99標準,但與Windows上的_snprintf不一樣,它在字符串沒法放入緩衝區時,返回一個負數。)
五:區域無關的字符串處理函數
有時當實現基於ASCII的協議時,但願可以處理ASCII中字符類型概念上的字符串,而無論當前的區域配置。Libevent提供了一些這樣的輔助函數。
int evutil_ascii_strcasecmp(const char *str1, const char *str2);
int evutil_ascii_strncasecmp(const char *str1, const char *str2, size_t n);
這些函數相似於strcasecmp和strncasecmp,但他們僅使用ASCII字符集進行比較,而無論當前的區域設置。
六:IPV6輔助函數和可移植函數
const char * evutil_inet_ntop(int af, const void *src, char *dst, size_t len);
int evutil_inet_pton(int af, const char *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。
int evutil_parse_sockaddr_port(const char *str, struct sockaddr*out, int * outlen);
該函數解析str中的字符串地址,並將結果寫入到out中。outlen是一個值-結果參數,入參時指向一個out長度的整數,返回時變爲實際使用的字節數。該方法成功時返回0,失敗是返回-1.它能夠處理下面格式的地址:
· [ipv6]:port (as in "[ffff::]:80")
· ipv6 (as in "ffff::")
· [ipv6] (as in "[ffff::]")
· ipv4:port (as in "1.2.3.4:80")
· ipv4 (as in "1.2.3.4")
若是沒有給定port,則在sockaddr的結果中,port被置爲0.
int evutil_sockaddr_cmp(const struct sockaddr *sa1,
const struct sockaddr*sa2, int include_port);
evutil_sockaddr_cmp函數比較兩個地址,若是sa1在sa2前面,則返回負數,若是它們相等就返回0,若是sa2在sa1前面,就返回正數。該函數能夠處理AF_INET和AF_INET6地址族,而對於其餘地址則是未定義的。它保證給出一個全部地址的總排序,可是不一樣的Libevent版本可能會有不一樣的排序結果。
若是include_port置爲false,那麼若是兩個sockaddrs僅在port不一樣的狀況下,該函數將他們視爲相同的。不然,若是include_port置爲true,則會視爲不一樣的。
七:結構體可移植函數
#define evutil_offsetof(type, field) /* ... */
相似於標準的offsetof宏,該宏返回field域在type中的偏移字節。
八:安全的隨機數生成器
不少應用都須要一個難以預測的隨機數源來保證它們的安全性。
void evutil_secure_rng_get_bytes(void * buf, size_t n);
該接口將n個字節的隨機數據填充到buf中。若是平臺提供了arc4random函數,則Libevent會使用該函數。不然的話,Libevent使用本身實現的arc4random函數。種子則來自操做系統的熵池(entropy pool)(Windows中的CryptGenRandom,其餘平臺中的/dev/urandom)
int evutil_secure_rng_init(void);
void evutil_secure_rng_add_bytes(const char * dat, size_t datlen);
通常不須要手動初始化安全隨機數生成器,可是若是須要保證它確實成功的初始化,能夠調用evutil_secure_rng_init()函數。它會seed RNG(隨機數生成器)(若是尚未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