定義
#ifdef WIN32
#define evutil_socket_t intptr_t
#else
#define evutil_socket_t int
#endif
這個類型在2.0.1-alpha版本中引入。
1.2 標準整數類型
落后于21世紀的C系統常常沒有實現C99標準規定的stdint.h頭文件??紤]到這種情況,libevent定義了來自于stdint.h的、位寬度確定(bit-width-specific)的整數類型:

跟C99標準一樣,這些類型都有明確的位寬度。
這些類型由1.4.0-alpha版本引入。MAX/MIN常量首次出現在2.0.4-alpha版本。
1.3 各種兼容性類型
在有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版本。
2 定時器可移植函數
不是每個平臺都定義了標準timeval操作函數,所以libevent也提供了自己的實現。
接口
#define evutil_timeradd(tvp, uvp, vvp) /*
*/
#define evutil_timersub(tvp, uvp, vvp) /*
*/
這些宏分別對前兩個參數進行加或者減運算,將結果存放到第三個參數中。
接口
#define evutil_timerclear(tvp) /*
*/
#define evutil_timerisset(tvp) /*
*/
清除timeval會將其值設置為0。evutil_timerisset宏檢查timeval是否已經設置,如果已經設置為非零值,返回ture,否則返回false。
接口
#define evutil_timercmp(tvp, uvp, cmp)
evutil_timercmp宏比較兩個timeval,如果其關系滿足cmp關系運算符,返回true。比如說,evutil_timercmp(t1,t2,<=)的意思是“是否t1<=t2?”。注意:與某些操作系統版本不同的是,libevent的時間比較支持所有C關系運算符(也就是<、>、==、!=、<=和>=)。
接口
int evutil_gettimeofday(struct timeval *tv, struct timezone *tz);
evutil_gettimeofdy()函數設置tv為當前時間,tz參數未使用。
示例
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之前的版本中使用<=或者>=是不安全的。
3 套接字API兼容性
本節由于歷史原因而存在:Windows從來沒有以良好兼容的方式實現Berkeley套接字API。
接口
int evutil_closesocket(evutil_socket_t s);
#define EVUTIL_CLOSESOCKET(s) evutil_closesocket(s)
這個接口用于關閉套接字。在Unix中,它是close()的別名;在Windows中,它調用closesocket()。(在Windows中不能將close()用于套接字,也沒有其他系統定義了closesocket())
evutil_closesocket()函數在2.0.5-alpha版本引入。在此之前,需要使用EVUTIL_CLOSESOCKET宏。
接口
#define EVUTIL_SOCKET_ERROR()
#define EVUTIL_SET_SOCKET_ERROR(errcode)
#define evutil_socket_geterror(sock)
#define evutil_socket_error_to_string(errcode)
這些宏訪問和操作套接字錯誤代碼。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錯誤是不同的。
接口
int evutil_make_socket_nonblocking(evutil_socket_t sock);
用于對套接字進行非阻塞IO的調用也不能移植到Windows中。evutil_make_socket_nonblocking()函數要求一個套接字(來自socket()或者accept())作為參數,將其設置為非阻塞的。(設置Unix中的O_NONBLOCK標志和Windows中的FIONBIO標志)
接口
int evutil_make_listen_socket_reuseable(evutil_socket_t sock);
這個函數確保關閉監聽套接字后,它使用的地址可以立即被另一個套接字使用。(在Unix中它設置SO_REUSEADDR標志,在Windows中則不做任何操作。不能在Windows中使用SO_REUSEADDR標志:它有另外不同的含義(譯者注:多個套接字綁定到相同地址))
接口
int evutil_make_socket_closeonexec(evutil_socket_t sock);
這個函數告訴操作系統,如果調用了exec(),應該關閉指定的套接字。在Unix中函數設置FD_CLOEXEC標志,在Windows上則沒有操作。
接口
int evutil_socketpair(int family, 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版本引入。
4 可移植的字符串操作函數
接口
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接口相同。函數返回在緩沖區足夠長的情況下將寫入的字節數,不包括結尾的NULL字節。(這個行為遵循C99的snprintf()標準,但與Windows的_snprintf()相反:如果字符串無法放入緩沖區,_snprintf()會返回負數)
evutil_strtoll()從1.4.2-rc版本就存在了,其他函數首次出現在1.4.5版本中。
5 區域無關的字符串操作函數
實現基于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字符集進行比較,而不管當前的區域設置。這兩個函數首次在2.0.3-alpha版本出現。
6 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);
這些函數根據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。
接口
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。
接口
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版本中引入。
7 結構體可移植性函數
接口
#define evutil_offsetof(type, field) /*
*/ 跟標準offsetof宏一樣,這個宏返回從type類型開始處到field字段的字節數。
這個宏由2.0.1-alpha版本引入,但2.0.3-alpha版本之前是有bug的。
8 安全隨機數發生器
很多應用(包括evdns)為了安全考慮需要很難預測的隨機數。
接口
void evutil_secure_rng_get_bytes(void *buf, size_t n);
這個函數用隨機數據填充buf處的n個字節。
如果所在平臺提供了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()。函數會播種RNG(如果沒有播種過),并在成功時返回0。函數返回-1則表示libevent無法在操作系統中找到合適的熵源(source of entropy),如果不自己初始化RNG,就無法安全使用RNG了。
如果程序運行在可能會放棄權限的環境中(比如說,通過執行chroot()),在放棄權限前應該調用evutil_secure_rng_init()。
可以調用evutil_secure_rng_add_bytes()向熵池加入更多隨機字節,但通常不需要這么做。
這些函數是2.0.4-alpha版本引入的。