目錄html
聲明: 該博客來源於傳智播客C++學院相關培訓參考手冊c++
頭文件<event2/util.h>
定義了許多可以幫助咱們利用libevent
實現可移植應用程序的函數。libevent會在庫內部使用這些函數。api
在除Windows以外的大多數地方,套接字是個整數,操做系統按照數值次序進行處理。然而,使用Windows套接字API時,socket具備類型SOCKET,它其實是個相似指針的句柄,收到這個句柄的次序是未定義的。在Windows中,libevent定義evutil_socket_t類型爲整型指針,能夠處理socket()或者accept()的輸出,而沒有指針截斷的風險。安全
/** * A type wide enough to hold the output of "socket()" or "accept()". On * Windows, this is an intptr_t; elsewhere, it is an int. */ #ifdef WIN32 #define evutil_socket_t intptr_t #else #define evutil_socket_t int #endif
這個類型在2.0.1-alpha版本中引入。app
落後於21世紀的C系統經常沒有實現C99標準規定的stdint.h
頭文件。考慮到這種狀況,libevent
定義了來自於stdint.h
的、位寬度肯定(bit-width-specific
)的整數類型:
less
跟C99標準同樣,這些類型都有明確的位寬度。dom
這些類型由1.4.0-alpha版本引入。MAX/MIN常量首次出如今2.0.4-alpha版本。socket
在有ssize_t
(有符號的size_t
)類型的平臺上,ev_ssize_t
定義爲ssize_t
;而在沒有的平臺上,則定義爲某合理的默認類型。ev_ssize_t
類型的最大可能值是EV_SSIZE_MAX
;最小可能值是EV_SSIZE_MIN
。(在平臺沒有定義SIZE_MAX
的時候,size_t
類型的最大可能值是EV_SIZE_MAX
)
ev_off_t
用於表明文件或者內存塊中的偏移量。在有合理off_t
類型定義的平臺,它被定義爲off_t
;在Windows上則定義爲ev_int64_t
。
某些套接字API定義了socklen_t
長度類型,有些則沒有定義。在有這個類型定義的平臺中,ev_socklen_t
定義爲socklen_t
,在沒有的平臺上則定義爲合理的默認類型。
ev_intptr_t
是一個有符號整數類型,足夠容納指針類型而不會產生截斷;而ev_uintptr_t
則是相應的無符號類型。
ev_ssize_t
類型由2.0.2-alpha版本加入。ev_socklen_t
類型由2.0.3-alpha版本加入。ev_intptr_t
與ev_uintptr_t類型,以及EV_SSIZE_MAX/MIN
宏定義由2.0.4-alpha版本加入。ev_off_t
類型首次出如今2.0.9-rc版本。ide
不是每一個平臺都定義了標準timeval
操做函數,因此libevent也提供了本身的實現。函數
這些宏分別對前兩個參數進行加或者減運算,將結果存放到第三個參數中。
/** * @name Manipulation macros for struct timeval. * * We define replacements * for timeradd, timersub, timerclear, timercmp, and timerisset. * * @{ */ #ifdef _EVENT_HAVE_TIMERADD #define evutil_timeradd(tvp, uvp, vvp) timeradd((tvp), (uvp), (vvp)) #define evutil_timersub(tvp, uvp, vvp) timersub((tvp), (uvp), (vvp)) #else #define evutil_timeradd(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ if ((vvp)->tv_usec >= 1000000) { \ (vvp)->tv_sec++; \ (vvp)->tv_usec -= 1000000; \ } \ } while (0) #define evutil_timersub(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ if ((vvp)->tv_usec < 0) { \ (vvp)->tv_sec--; \ (vvp)->tv_usec += 1000000; \ } \ } while (0) #endif /* !_EVENT_HAVE_HAVE_TIMERADD */ #ifdef _EVENT_HAVE_TIMERCLEAR #define evutil_timerclear(tvp) timerclear(tvp) #else #define evutil_timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 #endif
清除timeval會將其值設置爲0。evutil_timerisset宏檢查timeval是否已經設置,若是已經設置爲非零值,返回ture,不然返回false。
#ifdef _EVENT_HAVE_TIMERCLEAR #define evutil_timerclear(tvp) timerclear(tvp) #else #define evutil_timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 #endif #ifdef _EVENT_HAVE_TIMERISSET #define evutil_timerisset(tvp) timerisset(tvp) #else #define evutil_timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) #endif
evutil_timercmp宏比較兩個timeval,若是其關係知足cmp關係運算符,返回true。好比說,evutil_timercmp(t1,t2,<=)的意思是「是否t1<=t2?」。注意:與某些操做系統版本不一樣的是,libevent的時間比較支持全部C關係運算符(也就是<、>、==、!=、<=和>=)。
/** Return true iff the tvp is related to uvp according to the relational * operator cmp. Recognized values for cmp are ==, <=, <, >=, and >. */ #define evutil_timercmp(tvp, uvp, cmp) \ (((tvp)->tv_sec == (uvp)->tv_sec) ? \ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ ((tvp)->tv_sec cmp (uvp)->tv_sec))
evutil_gettimeofdy()函數設置tv爲當前時間,tz參數未使用。
/** Replacement for gettimeofday on platforms that lack it. */ #ifdef _EVENT_HAVE_GETTIMEOFDAY #define evutil_gettimeofday(tv, tz) gettimeofday((tv), (tz)) #else struct timezone; int evutil_gettimeofday(struct timeval *tv, struct timezone *tz); #endif
參考示例:
struct timeval tv1, tv2, tv3; /* Set tv1 = 5.5 seconds */ tv1.tv_sec = 5; tv1.tv_usec = 500*1000; /* Set tv2 = now */ evutil_gettimeofday(&tv2, NULL); /* Set tv3 = 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 the present."); if (evutil_timercmp(&tv1, &tv2, <)) /* == "If tv1 < tv2" */ puts("It is no longer the past.");
除evutil_gettimeofday()
由2.0版本引入外,這些函數由1.4.0-beta版本引入。
注意:在1.4.4以前的版本中使用<=或者>=是不安全的。
本節因爲歷史緣由而存在:Windows歷來沒有以良好兼容的方式實現Berkeley套接字API。 下面是一些用戶使其兼容的函數接口
/** Do the platform-specific call needed to close a socket returned from socket() or accept(). @param sock The socket to be closed @return 0 on success, -1 on failure */ int evutil_closesocket(evutil_socket_t sock); #define EVUTIL_CLOSESOCKET(s) evutil_closesocket(s)
這個接口用於關閉socket套接字。在Unix中,它是close()
的別名;在Windows中,它調用closesocket()
。(在Windows中不能將close()
用於套接字,也沒有其餘系統定義了closesocket()
)
evutil_closesocket()
函數在2.0.5-alpha版本引入。在此以前,須要使用EVUTIL_CLOSESOCKET
宏。
#ifdef WIN32 /** Return the most recent socket error. Not idempotent on all platforms. */ #define EVUTIL_SOCKET_ERROR() WSAGetLastError() /** Replace the most recent socket error with errcode */ #define EVUTIL_SET_SOCKET_ERROR(errcode) \ do { WSASetLastError(errcode); } while (0) /** Return the most recent socket error to occur on sock. */ int evutil_socket_geterror(evutil_socket_t sock); /** Convert a socket error to a string. */ const char *evutil_socket_error_to_string(int errcode); #elif defined(_EVENT_IN_DOXYGEN) /** @name Socket error functions These functions are needed for making programs compatible between Windows and Unix-like platforms. You see, Winsock handles socket errors differently from the rest of the world. Elsewhere, a socket error is like any other error and is stored in errno. But winsock functions require you to retrieve the error with a special function, and don't let you use strerror for the error codes. And handling EWOULDBLOCK is ... different. @{ */ /** Return the most recent socket error. Not idempotent on all platforms. */ #define EVUTIL_SOCKET_ERROR() ... /** Replace the most recent socket error with errcode */ #define EVUTIL_SET_SOCKET_ERROR(errcode) ... /** Return the most recent socket error to occur on sock. */ #define evutil_socket_geterror(sock) ... /** Convert a socket error to a string. */ #define evutil_socket_error_to_string(errcode) ... /**@}*/ #else #define EVUTIL_SOCKET_ERROR() (errno) #define EVUTIL_SET_SOCKET_ERROR(errcode) \ do { errno = (errcode); } while (0) #define evutil_socket_geterror(sock) (errno) #define evutil_socket_error_to_string(errcode) (strerror(errcode)) #endif
這些宏用於訪問和操做套接字錯誤代碼。EVUTIL_SOCKET_ERROR()
返回本線程最後一次套接字操做的全局錯誤號,evutil_socket_geterror()
則返回某特定套接字的錯誤號。(在類Unix系統中都是errno
)EVUTIL_SET_SOCKET_ERROR()
修改當前套接字錯誤號(與設置Unix中的errno
相似),evutil_socket_error_to_string()
返回表明某給定套接字錯誤號的字符串(與Unix中的strerror()
相似)。
(由於對於來自套接字函數的錯誤,Windows不使用errno
,而是使用WSAGetLastError()
,因此須要這些函數。)
注意:Windows套接字錯誤與從errno看到的標準C錯誤是不一樣的。
/** Do platform-specific operations as needed to make a socket nonblocking. @param sock The socket to make nonblocking @return 0 on success, -1 on failure */ int evutil_make_socket_nonblocking(evutil_socket_t sock);
用於對套接字進行非阻塞IO的調用也不能移植到Windows中。evutil_make_socket_nonblocking()函數要求一個套接字(來自socket()或者accept())做爲參數,將其設置爲非阻塞的。(設置Unix中的O_NONBLOCK標誌和Windows中的FIONBIO標誌)
/** Do platform-specific operations to make a listener socket reusable. Specifically, we want to make sure that another program will be able to bind this address right after we've closed the listener. This differs from Windows's interpretation of "reusable", which allows multiple listeners to bind the same address at the same time. @param sock The socket to make reusable @return 0 on success, -1 on failure */ int evutil_make_listen_socket_reuseable(evutil_socket_t sock);
這個函數確保關閉監聽套接字後,它使用的地址能夠當即被另外一個套接字使用。(在Unix中它設置SO_REUSEADDR
標誌,在Windows中則不作任何操做。不能在Windows中使用SO_REUSEADDR
標誌:它有另外不一樣的含義(譯者注:多個套接字綁定到相同地址))
/** Do platform-specific operations as needed to close a socket upon a successful execution of one of the exec*() functions. @param sock The socket to be closed @return 0 on success, -1 on failure */ int evutil_make_socket_closeonexec(evutil_socket_t sock);
這個函數告訴操做系統,若是調用了exec()
,應該關閉指定的套接字。在Unix中函數設置FD_CLOEXEC
標誌,在Windows上則沒有操做。
/** Create two new sockets that are connected to each other. On Unix, this simply calls socketpair(). On Windows, it uses the loopback network interface on 127.0.0.1, and only AF_INET,SOCK_STREAM are supported. (This may fail on some Windows hosts where firewall software has cleverly decided to keep 127.0.0.1 from talking to itself.) Parameters and return values are as for socketpair() */ int evutil_socketpair(int d, int type, int protocol, evutil_socket_t sv[2]);
這個函數的行爲跟Unix的socketpair()
調用相同:建立兩個相互鏈接起來的套接字,可對其使用普通套接字IO調用。函數將兩個套接字存儲在sv[0]
和sv[1]
中,成功時返回0,失敗時返回-1。
在Windows中,這個函數僅能支持AF_INET
協議族、SOCK_STREAM
類型和0
協議的套接字。注意:在防火牆軟件明確阻止127.0.0.1
,禁止主機與自身通話的狀況下,函數可能失敗。
除了evutil_make_socket_closeonexec()
由2.0.4-alpha版本引入外,這些函數都由1.4.0-alpha版本引入。
/* big-int related functions */ /** Parse a 64-bit value from a string. Arguments are as for strtol. */ ev_int64_t evutil_strtoll(const char *s, char **endptr, int base);
這個函數與strtol
行爲相同,只是用於64位整數。在某些平臺上,僅支持十進制。
/** Replacement for snprintf to get consistent behavior on platforms for which the return value of snprintf does not conform to C99. */ int evutil_snprintf(char *buf, size_t buflen, const char *format, ...) #ifdef __GNUC__ __attribute__((format(printf, 3, 4))) #endif ; /** Replacement for vsnprintf to get consistent behavior on platforms for which the return value of snprintf does not conform to C99. */ int evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap) #ifdef __GNUC__ __attribute__((format(printf, 3, 0))) #endif ;
這些snprintf
替代函數的行爲與標準snprintf
和vsnprintf
接口相同。函數返回在緩衝區足夠長的狀況下將寫入的字節數,不包括結尾的NULL字節。(這個行爲遵循C99
的snprintf()
標準,但與Windows的_snprintf()
相反:若是字符串沒法放入緩衝區,_snprintf()
會返回負數)
evutil_strtoll()
從1.4.2-rc版本就存在了,其餘函數首次出如今1.4.5版本中。
實現基於ASCII的協議時,可能想要根據字符類型的ASCII記號來操做字符串,而無論當前的區域設置。libevent爲此提供了一些函數:
/** As strcasecmp, but always compares the characters in locale-independent ASCII. That's useful if you're handling data in ASCII-based protocols. */ int evutil_ascii_strcasecmp(const char *str1, const char *str2); /** As strncasecmp, but always compares the characters in locale-independent ASCII. That's useful if you're handling data in ASCII-based protocols. */ int evutil_ascii_strncasecmp(const char *str1, const char *str2, size_t n);
這些函數與strcasecmp()
和strncasecmp()
的行爲相似,只是它們老是使用ASCII字符集進行比較,而無論當前的區域設置。這兩個函數首次在2.0.3-alpha版本出現。
/** Replacement for inet_ntop for platforms which lack it. */ const char *evutil_inet_ntop(int af, const void *src, char *dst, size_t len); /** Replacement for inet_pton for platforms which lack it. */ int evutil_inet_pton(int af, const char *src, void *dst);
這些函數根據RFC 3493的規定解析和格式化IPv4與IPv6地址,與標準inet_ntop()
和inet_pton()
函數行爲相同。要格式化IPv4地址,調用evutil_inet_ntop()
,設置af
爲AF_INET
,src
指向in_addr
結構體,dst
指向大小爲len
的字符緩衝區。對於IPv6地址,af
應該是AF_INET6
,src
則指向in6_addr
結構體。要解析IP地址,調用evutil_inet_pton()
,設置af
爲AF_INET
或者AF_INET6
,src
指向要解析的字符串,dst
指向一個in_addr
或者in_addr6
結構體。
失敗時evutil_inet_ntop()
返回NULL
,成功時返回到dst
的指針。成功時evutil_inet_pton()
返回0,失敗時返回-1。
/** Parse an IPv4 or IPv6 address, with optional port, from a string. Recognized formats are: - [IPv6Address]:port - [IPv6Address] - IPv6Address - IPv4Address:port - IPv4Address If no port is specified, the port in the output is set to 0. @param str The string to parse. @param out A struct sockaddr to hold the result. This should probably be a struct sockaddr_storage. @param outlen A pointer to the number of bytes that that 'out' can safely hold. Set to the number of bytes used in 'out' on success. @return -1 if the address is not well-formed, if the port is out of range, or if out is not large enough to hold the result. Otherwise returns 0 on success. */ int evutil_parse_sockaddr_port(const char *str, struct sockaddr *out, int *outlen);
這個接口解析來自str
的地址,將結果寫入到out
中。outlen
參數應該指向一個表示out
中可用字節數的整數;函數返回時這個整數將表示實際使用了的字節數。成功時函數返回0,失敗時返回-1。函數識別下列地址格式:
l [ipv6]:端口號(如[ffff::]:80)
l ipv6(如ffff::)
l [ipv6](如[ffff::])
l ipv4:端口號(如1.2.3.4:80)
l ipv4(如1.2.3.4)
若是沒有給出端口號,結果中的端口號將被設置爲0。
/** Compare two sockaddrs; return 0 if they are equal, or less than 0 if sa1 * preceeds sa2, or greater than 0 if sa1 follows sa2. If include_port is * true, consider the port as well as the address. Only implemented for * AF_INET and AF_INET6 addresses. The ordering is not guaranteed to remain * the same between Libevent versions. */ 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地址;對於其餘地址,返回值未定義。函數確保考慮地址的完整次序,可是不一樣版本中的次序可能不一樣。
若是include_port參數爲false,而兩個地址只有端口號不一樣,則它們被認爲是相等的。不然,具備不一樣端口號的地址被認爲是不等的。
除evutil_sockaddr_cmp()在2.0.3-alpha版本引入外,這些函數在2.0.1-alpha版本中引入。
/** Replacement for offsetof on platforms that don't define it. */ #ifdef offsetof #define evutil_offsetof(type, field) offsetof(type, field) #else #define evutil_offsetof(type, field) ((off_t)(&((type *)0)->field)) #endif
跟標準offsetof
宏同樣,這個宏返回從type
類型開始處到field
字段的字節數。
這個宏由2.0.1-alpha版本引入,但2.0.3-alpha版本以前是有bug的。
不少應用(包括evdns
)爲了安全考慮須要很難預測的隨機數。
/** Generate n bytes of secure pseudorandom data, and store them in buf. * * Current versions of Libevent use an ARC4-based random number generator, * seeded using the platform's entropy source (/dev/urandom on Unix-like * systems; CryptGenRandom on Windows). This is not actually as secure as it * should be: ARC4 is a pretty lousy cipher, and the current implementation * provides only rudimentary prediction- and backtracking-resistance. Don't * use this for serious cryptographic applications. */ void evutil_secure_rng_get_bytes(void *buf, size_t n);
這個函數用隨機數據填充buf
處的n
個字節。
若是所在平臺提供了arc4random()
,libevent會使用這個函數。不然,libevent會使用本身的arc4random()
實現,種子則來自操做系統的熵池(entropy pool
)(Windows中的CryptGenRandom
,其餘平臺中的/dev/urandom
)
/** * Seed the secure random number generator if needed, and return 0 on * success or -1 on failure. * * It is okay to call this function more than once; it will still return * 0 if the RNG has been successfully seeded and -1 if it can't be * seeded. * * Ordinarily you don't need to call this function from your own code; * Libevent will seed the RNG itself the first time it needs good random * numbers. You only need to call it if (a) you want to double-check * that one of the seeding methods did succeed, or (b) you plan to drop * the capability to seed (by chrooting, or dropping capabilities, or * whatever), and you want to make sure that seeding happens before your * program loses the ability to do it. */ int evutil_secure_rng_init(void); /** * Set a filename to use in place of /dev/urandom for seeding the secure * PRNG. Return 0 on success, -1 on failure. * * Call this function BEFORE calling any other initialization or RNG * functions. * * (This string will _NOT_ be copied internally. Do not free it while any * user of the secure RNG might be running. Don't pass anything other than a * real /dev/...random device file here, or you might lose security.) * * This API is unstable, and might change in a future libevent version. */ int evutil_secure_rng_set_urandom_device_file(char *fname); /** Seed the random number generator with extra random bytes. You should almost never need to call this function; it should be sufficient to invoke evutil_secure_rng_init(), or let Libevent take care of calling evutil_secure_rng_init() on its own. If you call this function as a _replacement_ for the regular entropy sources, then you need to be sure that your input contains a fairly large amount of strong entropy. Doing so is notoriously hard: most people who try get it wrong. Watch out! @param dat a buffer full of a strong source of random numbers @param datlen the number of bytes to read from datlen */ void evutil_secure_rng_add_bytes(const char *dat, size_t datlen);
不須要手動初始化安全隨機數發生器,可是若是要確認已經成功初始化,能夠調用evutil_secure_rng_init()
。函數會播種RNG
(若是沒有播種過),並在成功時返回0。函數返回-1則表示libevent沒法在操做系統中找到合適的熵源(source of entropy),若是不本身初始化RNG,就沒法安全使用RNG了。
若是程序運行在可能會放棄權限的環境中(好比說,經過執行chroot()
),在放棄權限前應該調用evutil_secure_rng_init()
。
能夠調用evutil_secure_rng_add_bytes()
向熵池加入更多隨機字節,但一般不須要這麼作。
這些函數是2.0.4-alpha版本引入的。
相關書籍: http://www.wangafu.net/~nickm/libevent-book/Ref2_eventbase.html
官方參考網站: https://www.monkey.org/~provos/libevent/doxygen-2.0.1/index.html