Libevent:6辅助函数以及类型
? ? ? ? ?在頭文件<event2/util.h>中定義了許多有用的函數和類型來幫助實現可移植的程序。Libevent在內部使用這些類型和函數。
?
一:基本類型
evutil_socket_t
???????? 除了Windows之外的大多數系統,socket就是一個整數,而且操作系統按照數值順序對它們進行處理。而在Windows socket API中,socket是SOCKET類型,該類型是一個類似于指針的OS句柄,而且得到它們的順序也是未定義的。Libevent定義evutil_socket_t類型為一個整數,該整數可以表示socket或者accept函數的返回值,并且可以在Windows上避免指針截斷的風險。
#ifdef?WIN32
#define?evutil_socket_t ?intptr_t
#else
#define?evutil_socket_t ?int
#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 |
???????? 類似于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
轉載于:https://www.cnblogs.com/gqtcgq/p/7247257.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Libevent:6辅助函数以及类型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于智能的网络空间安全防御
- 下一篇: 巧妙的有css合并图片解决tab切换的背